From 3e33eb58037a3a9a50867fd606496acca9896ba1 Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:05:43 +0800 Subject: [PATCH] fix(workbench): require terminal authority before sealing turn (#2041) --- internal/cloud/workbench-turn-projection.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/internal/cloud/workbench-turn-projection.ts b/internal/cloud/workbench-turn-projection.ts index 1b89e9f0..08b3dcee 100644 --- a/internal/cloud/workbench-turn-projection.ts +++ b/internal/cloud/workbench-turn-projection.ts @@ -157,8 +157,14 @@ export function traceTerminalEvidence(trace = null) { } const finalResponse = objectValue(trace?.finalResponse); if (finalResponse) { - const status = terminalStatusFromValue(finalResponse.status ?? trace?.status) ?? "completed"; - return { source: "trace-final-response", status, evidence: { textPresent: Boolean(projectionText(finalResponse)), valuesRedacted: true }, valuesRedacted: true }; + // finalResponse is display text evidence only. AgentRun may surface an + // assistant/finalResponse payload while the runner is still draining tools + // or waiting for its terminal report; using that text as lifecycle authority + // seals the Workbench card too early and makes terminal elapsed jump later. + if (trace?.terminal === true || trace?.sealed === true || firstTimestamp(trace?.finishedAt, trace?.completedAt, trace?.endedAt)) { + const status = terminalStatusFromValue(finalResponse.status ?? trace?.status) ?? "completed"; + return { source: "trace-final-response", status, evidence: { textPresent: Boolean(projectionText(finalResponse)), valuesRedacted: true }, valuesRedacted: true }; + } } const events = Array.isArray(trace?.events) ? trace.events : []; return terminalTraceEventEvidence(events); @@ -197,8 +203,11 @@ function resultHasTerminalAuthority(result = null, traceTerminal = null) { if (result.terminal === true || result.sealed === true) return true; if (result.error || result.blocker) return true; if (firstTimestamp(result.finishedAt, result.completedAt, result.endedAt)) return true; - const finalText = projectionText(result.finalResponse, result.assistantText, result.reply, result.text, result.summary); - return Boolean(finalText && terminalStatusFromValue(result.status)); + // A completed-looking status plus final text is not enough authority to seal + // a Workbench turn. For AgentRun-backed turns the final text can arrive before + // the runner terminal event; sealing here makes the visible completed card + // keep changing once later trace snapshots arrive. + return false; } function activeTurnEvidence({ result = null, session = null, trace = null } = {}) {