fix: converge workbench session state projection

This commit is contained in:
lyon
2026-06-18 15:55:18 +08:00
parent b625ae2499
commit dd01f47951
12 changed files with 276 additions and 46 deletions
+29 -3
View File
@@ -24,20 +24,24 @@ export const agentAPI = {
getAgentTrace: async (traceId: string, timeoutMs = 8000, activityRef?: ApiRequestOptions["activityRef"], options?: AgentTraceRequestOptions): Promise<ApiResult<AgentChatResultResponse>> => normalizeWorkbenchTraceResult(await fetchJson<Record<string, unknown>>(workbenchTracePath(traceId, options), { timeoutMs, timeoutName: "Code Agent trace", activityRef }), traceId),
createAgentSession: (payload: Record<string, unknown> = {}): Promise<ApiResult<{ session?: Record<string, unknown> }>> => fetchJson("/v1/agent/sessions", { method: "POST", body: JSON.stringify(payload), timeoutMs: 30000, timeoutName: "Code Agent session create" }),
getAgentSession: (sessionId: string): Promise<ApiResult<{ session?: Record<string, unknown> }>> => fetchJson(`/v1/agent/sessions/${encodeURIComponent(sessionId)}`, { timeoutMs: 8000, timeoutName: "Code Agent session" }),
deleteAgentSession: (sessionId: string): Promise<ApiResult<{ ok?: boolean; status?: string; session?: Record<string, unknown>; archivedCount?: number }>> => fetchJson(`/v1/agent/sessions/${encodeURIComponent(sessionId)}`, { method: "DELETE", timeoutMs: 30000, timeoutName: "Code Agent session delete" }),
cancelAgentMessage: (payload: Record<string, unknown>): Promise<ApiResult<{ ok?: boolean; canceled?: boolean; error?: { code?: string; message?: string; userMessage?: string } }>> => fetchJson("/v1/agent/chat/cancel", { method: "POST", body: JSON.stringify(payload), timeoutMs: 30000, timeoutName: "Code Agent cancel" })
};
function normalizeWorkbenchTurnResult(result: ApiResult<Record<string, unknown>>, traceId: string): ApiResult<AgentChatResultResponse> {
if (!result.ok || !result.data) return result as ApiResult<AgentChatResultResponse>;
const turn = recordValue(result.data.turn) ?? result.data;
const trace = recordValue(turn.trace);
const status = canonicalWorkbenchStatus(textValue(turn.status) ?? textValue(result.data.status) ?? "unknown", textValue(trace?.status, result.data.traceStatus));
const terminal = turn.terminal === true || isTerminalStatus(status);
return {
...result,
data: {
...turn,
traceId: textValue(turn.traceId) ?? traceId,
status: textValue(turn.status) ?? textValue(result.data.status) ?? "unknown",
running: turn.running === true,
terminal: turn.terminal === true,
status,
running: !terminal && (turn.running === true || isActiveStatus(status)),
terminal,
sessionId: textValue(turn.sessionId) ?? undefined,
threadId: textValue(turn.threadId) ?? undefined,
agentRun: recordValue(turn.agentRun) ?? undefined,
@@ -46,6 +50,28 @@ function normalizeWorkbenchTurnResult(result: ApiResult<Record<string, unknown>>
};
}
function canonicalWorkbenchStatus(primary: string | null, traceStatus: string | null): string {
const primaryStatus = normalizeStatus(primary);
const trace = normalizeStatus(traceStatus);
if (trace && isTerminalStatus(trace) && (!primaryStatus || isActiveStatus(primaryStatus) || !isTerminalStatus(primaryStatus))) return trace;
return primaryStatus ?? trace ?? "unknown";
}
function normalizeStatus(value: unknown): string | null {
const text = textValue(value);
if (!text) return null;
const normalized = text.toLowerCase().replace(/_/gu, "-");
return normalized === "cancelled" ? "canceled" : normalized;
}
function isActiveStatus(value: unknown): boolean {
return ["accepted", "active", "busy", "creating", "pending", "processing", "running"].includes(normalizeStatus(value) ?? "");
}
function isTerminalStatus(value: unknown): boolean {
return ["blocked", "canceled", "completed", "failed", "stale", "thread-resume-failed", "timeout"].includes(normalizeStatus(value) ?? "");
}
function normalizeWorkbenchTraceResult(result: ApiResult<Record<string, unknown>>, traceId: string): ApiResult<AgentChatResultResponse> {
if (!result.ok || !result.data) return result as ApiResult<AgentChatResultResponse>;
const events = Array.isArray(result.data.events) ? result.data.events : [];