fix: keep workbench cancel on durable projection (#2086)

This commit is contained in:
Lyon
2026-06-25 06:10:05 +08:00
committed by GitHub
parent 67282299c2
commit d818aad6d5
3 changed files with 17 additions and 18 deletions
+2 -2
View File
@@ -121,12 +121,12 @@ function traceEventsForDisplay(events: TraceEvent[] = []): TraceEvent[] {
}
function traceEventDisplaySeq(event: TraceEvent | null | undefined): number | null {
return numberOrNull(event?.projectedSeq) ?? numberOrNull(event?.seq);
return numberOrNull(event?.projectedSeq);
}
function traceEventIdentityToken(event: TraceEvent): string | number | null {
const seq = traceEventDisplaySeq(event);
return seq ?? nonEmptyString(event.projectedSeq ?? event.seq);
return seq ?? nonEmptyString(event.projectedSeq);
}
function isToolTraceRow(row: TraceEventRow): boolean {
@@ -218,14 +218,17 @@ function traceEventSortSeq(event: TraceEvent): number {
function traceEventIdentity(event: TraceEvent): string | null {
const label = typeof event.label === "string" ? event.label : typeof event.type === "string" ? event.type : "";
const seqValue = event.projectedSeq ?? event.seq;
const seqValue = event.projectedSeq;
const seq = typeof seqValue === "number" || typeof seqValue === "string" ? String(seqValue) : "";
if (seq) return `projected:${seq}:${label || "event"}`;
const source = typeof event.source === "string" ? event.source : "";
const sourceSeq = typeof event.sourceSeq === "number" || typeof event.sourceSeq === "string" ? String(event.sourceSeq) : "";
if (source && sourceSeq) return `source:${source}:${sourceSeq}:${label || "event"}`;
return null;
}
function traceEventProjectedSeq(event: TraceEvent): number {
const seq = Number(event.projectedSeq ?? event.seq);
const seq = Number(event.projectedSeq);
return Number.isFinite(seq) ? Math.trunc(seq) : Number.NaN;
}
+10 -14
View File
@@ -832,23 +832,19 @@ function nonBlockingProjection(projection: ProjectionDiagnostic | null): Project
async function cancelAgentMessage(message: ChatMessage): Promise<void> {
const traceId = message.traceId ?? message.runnerTrace?.traceId;
if (!traceId) return;
const sessionId = message.sessionId ?? selectedSessionId.value;
const response = await api.agent.cancelAgentMessage({ traceId, sessionId: message.sessionId ?? selectedSessionId.value, threadId: message.threadId ?? selectedThreadId.value });
const payload = response.data as AgentChatResultResponse | null;
const status = normalizedStatusText(payload?.status) ?? "canceled";
const terminal = payload?.terminal === true || isTerminalMessageStatus(status);
if (response.ok && payload) {
applyTurnStatusSnapshot(traceId, payload);
if (terminal) completeTrace(traceId, payload);
} else {
applyTurnStatusSnapshot(traceId, { traceId, status: "canceled", running: false, terminal: true, sessionId: message.sessionId ?? selectedSessionId.value ?? undefined, threadId: message.threadId ?? selectedThreadId.value ?? undefined } as AgentChatResultResponse);
}
if (status === "canceled" || status === "cancelled") {
markMessage(traceId, { status: "canceled", text: "用户已取消该 turn。" });
void clearActiveTrace(traceId, "cancel-agent-message");
if (!response.ok) {
applyProjectionDiagnostic(traceId, projectionDiagnosticFromApiFailure(response, { code: "code_agent_cancel_failed", message: response.error ?? "Code Agent 取消请求失败,当前 turn 状态保持 canonical projection。", health: response.status === 0 ? "unavailable" : "degraded" }));
return;
}
recordActivity("cancel:accepted");
if (message.status === "running") chatPending.value = false;
currentRequest.value = null;
scheduleSessionListRefresh(message.sessionId ?? selectedSessionId.value, SESSION_LIST_TERMINAL_REFRESH_DELAY_MS);
void clearActiveTrace(traceId, "cancel-agent-message");
void refreshMessageProjectionForTrace(sessionId, traceId);
if (sessionId) void hydrateTraceEvents(serverState.value.messagesBySessionId[sessionId] ?? messages.value);
scheduleSessionListRefresh(sessionId, SESSION_LIST_TERMINAL_REFRESH_DELAY_MS);
}
async function cancelRunningTrace(): Promise<void> {
@@ -2231,7 +2227,7 @@ function traceNextProjectedSeq(result: AgentChatResultResponse, fallback: number
}
function traceEventProjectedSeq(event: TraceEvent): number {
const seq = Number(event.projectedSeq ?? event.seq);
const seq = Number(event.projectedSeq);
return Number.isFinite(seq) ? Math.trunc(seq) : Number.NaN;
}