Normalise l'état Work pour empêcher l'affichage d'un onglet Work vide. QA VERT : tsc --noEmit exit 0 ; vitest run 42 fichiers / 407 tests passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
146 lines
4.4 KiB
TypeScript
146 lines
4.4 KiB
TypeScript
import type {
|
|
AgentTicketState,
|
|
AgentWorkState,
|
|
ConversationPreviewStatus,
|
|
ConversationTurnWorkPreview,
|
|
ConversationWorkSummary,
|
|
ProjectWorkState,
|
|
TicketWorkSource,
|
|
TicketWorkStatus,
|
|
WorkBusyState,
|
|
} from "@/domain";
|
|
|
|
type RecordLike = Record<string, unknown>;
|
|
|
|
function isRecord(value: unknown): value is RecordLike {
|
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
}
|
|
|
|
function stringValue(value: unknown, fallback = ""): string {
|
|
return typeof value === "string" ? value : fallback;
|
|
}
|
|
|
|
function numberValue(value: unknown, fallback = 0): number {
|
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
}
|
|
|
|
function arrayValue(value: unknown): unknown[] {
|
|
return Array.isArray(value) ? value : [];
|
|
}
|
|
|
|
function normalizeSource(value: unknown): TicketWorkSource {
|
|
if (!isRecord(value)) return { kind: "human" };
|
|
if (value.kind === "agent") {
|
|
return { kind: "agent", agentId: stringValue(value.agentId, "unknown") };
|
|
}
|
|
return { kind: "human" };
|
|
}
|
|
|
|
function normalizeTicketStatus(value: unknown): TicketWorkStatus {
|
|
return value === "inProgress" ? "inProgress" : "queued";
|
|
}
|
|
|
|
function normalizeBusy(value: unknown): WorkBusyState {
|
|
if (isRecord(value) && value.state === "busy") {
|
|
return {
|
|
state: "busy",
|
|
ticket: stringValue(value.ticket, "unknown"),
|
|
sinceMs: numberValue(value.sinceMs, 0),
|
|
};
|
|
}
|
|
return { state: "idle" };
|
|
}
|
|
|
|
function normalizeTicket(value: unknown, index: number): AgentTicketState {
|
|
const ticket = isRecord(value) ? value : {};
|
|
const taskPreview = stringValue(ticket.taskPreview);
|
|
return {
|
|
ticketId: stringValue(ticket.ticketId, `legacy-ticket-${index}`),
|
|
conversationId: stringValue(ticket.conversationId),
|
|
position: numberValue(ticket.position, index),
|
|
status: normalizeTicketStatus(ticket.status),
|
|
source: normalizeSource(ticket.source),
|
|
requesterLabel: stringValue(ticket.requesterLabel),
|
|
taskPreview,
|
|
taskLen: numberValue(ticket.taskLen, taskPreview.length),
|
|
};
|
|
}
|
|
|
|
function normalizeAgent(value: unknown, index: number): AgentWorkState {
|
|
const agent = isRecord(value) ? value : {};
|
|
const liveKind: "pty" | "structured" =
|
|
isRecord(agent.live) && agent.live.kind === "structured" ? "structured" : "pty";
|
|
const live = isRecord(agent.live)
|
|
? {
|
|
nodeId: stringValue(agent.live.nodeId),
|
|
sessionId: stringValue(agent.live.sessionId),
|
|
kind: liveKind,
|
|
}
|
|
: undefined;
|
|
|
|
return {
|
|
agentId: stringValue(agent.agentId, `legacy-agent-${index}`),
|
|
name: stringValue(agent.name, "Unnamed agent"),
|
|
profileId: stringValue(agent.profileId, "unknown"),
|
|
...(live ? { live } : {}),
|
|
busy: normalizeBusy(agent.busy),
|
|
tickets: arrayValue(agent.tickets).map(normalizeTicket),
|
|
};
|
|
}
|
|
|
|
function normalizeSummaryStatus(value: unknown): ConversationPreviewStatus {
|
|
if (
|
|
value === "ready" ||
|
|
value === "partial" ||
|
|
value === "unavailable" ||
|
|
value === "missing"
|
|
) {
|
|
return value;
|
|
}
|
|
return "missing";
|
|
}
|
|
|
|
function normalizeTurn(value: unknown, index: number): ConversationTurnWorkPreview {
|
|
const turn = isRecord(value) ? value : {};
|
|
const textPreview = stringValue(turn.textPreview);
|
|
const role =
|
|
turn.role === "response" || turn.role === "toolActivity"
|
|
? turn.role
|
|
: "prompt";
|
|
return {
|
|
role,
|
|
source: normalizeSource(turn.source),
|
|
atMs: numberValue(turn.atMs, index),
|
|
textPreview,
|
|
textLen: numberValue(turn.textLen, textPreview.length),
|
|
};
|
|
}
|
|
|
|
function nullableString(value: unknown): string | null {
|
|
return typeof value === "string" ? value : null;
|
|
}
|
|
|
|
function normalizeConversation(
|
|
value: unknown,
|
|
index: number,
|
|
): ConversationWorkSummary {
|
|
const summary = isRecord(value) ? value : {};
|
|
return {
|
|
conversationId: stringValue(summary.conversationId, `legacy-conversation-${index}`),
|
|
status: normalizeSummaryStatus(summary.status),
|
|
objectivePreview: nullableString(summary.objectivePreview),
|
|
summaryPreview: nullableString(summary.summaryPreview),
|
|
summaryLen: numberValue(summary.summaryLen, 0),
|
|
upTo: nullableString(summary.upTo),
|
|
recentTurns: arrayValue(summary.recentTurns).map(normalizeTurn),
|
|
};
|
|
}
|
|
|
|
export function normalizeProjectWorkState(value: unknown): ProjectWorkState {
|
|
const state = isRecord(value) ? value : {};
|
|
return {
|
|
agents: arrayValue(state.agents).map(normalizeAgent),
|
|
conversations: arrayValue(state.conversations).map(normalizeConversation),
|
|
};
|
|
}
|