From c58a7fff6cdae752352cdd7720740017b0d54330 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Sat, 6 Jun 2026 14:20:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B6=E6=95=9B=E5=85=AC=E5=85=B1=20t?= =?UTF-8?q?race=20result=20=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/reference/spec-v02-code-agent-trace.md | 2 ++ internal/cloud/code-agent-chat.ts | 1 - internal/cloud/code-agent-session-lifecycle.ts | 1 - internal/cloud/server-agent-chat.test.ts | 8 ++++++++ internal/cloud/server-code-agent-http.ts | 16 ++++++++++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/reference/spec-v02-code-agent-trace.md b/docs/reference/spec-v02-code-agent-trace.md index 7417b1ce..587770c9 100644 --- a/docs/reference/spec-v02-code-agent-trace.md +++ b/docs/reference/spec-v02-code-agent-trace.md @@ -46,6 +46,8 @@ 5. 写回内存缓存和 session 派生证据时,只能写刚由 command result 生成的 payload;旧 `finalResponse`、旧 `traceSummary` 和旧 `traceResults` 不得参与 merge 覆盖。 6. 如果 command registry 找不到该 `traceId` 对应 command,返回结构化错误,例如 `agentrun_trace_command_not_found`;不得回退到旧缓存、top-level snapshot、latest trace 或其他 command result。 +Completed result 响应必须把 AgentRun command result 的终态证据公开为 `terminalEvidence`,并与 `agentRun.commandId`、`finalResponse.traceId` 保持一致。`/result` 和 `/trace` 响应不得导出 `fallback` 字段;降级、缺失或过期只能用 `status`、`traceStatus`、`retention`、`error` 和 `terminalEvidence` 表达,避免旧 fallback contract 重新成为并行 final response 路径。 + Running 轮询可以返回 `202` 和当前 runnerTrace;这只是“尚未 terminal”的轮询语义,不是 final fallback。Completed / failed / canceled / blocked 等 terminal 状态必须经过同一 command registry/result 路径确认。 ## Trace 读取与渲染流程 diff --git a/internal/cloud/code-agent-chat.ts b/internal/cloud/code-agent-chat.ts index 1d31e187..3c48a6f2 100644 --- a/internal/cloud/code-agent-chat.ts +++ b/internal/cloud/code-agent-chat.ts @@ -455,7 +455,6 @@ function decorateChatSessionLifecycle(payload) { sessionSummary: summary, sessionReused: summary.reused, isNewSession: summary.newSession, - fallbackUsed: summary.fallback, degraded: summary.degraded, degradationReason: summary.reason, degradationReasonCode: summary.reasonCode diff --git a/internal/cloud/code-agent-session-lifecycle.ts b/internal/cloud/code-agent-session-lifecycle.ts index d5f3106d..ba3d1841 100644 --- a/internal/cloud/code-agent-session-lifecycle.ts +++ b/internal/cloud/code-agent-session-lifecycle.ts @@ -112,7 +112,6 @@ export function codeAgentSessionLifecycleSummary(input = {}) { busy: status === "busy", requiresNewSession, degraded, - fallback: fallbackUsed, unsupported: input.unsupported === true, reasonCode: reasonCode ?? null, reason: reason ?? null, diff --git a/internal/cloud/server-agent-chat.test.ts b/internal/cloud/server-agent-chat.test.ts index 72b51276..b41f37f4 100644 --- a/internal/cloud/server-agent-chat.test.ts +++ b/internal/cloud/server-agent-chat.test.ts @@ -1990,9 +1990,13 @@ test("cloud api repairs historical same-session AgentRun trace after lastTraceId assert.equal(secondResult.status, "completed"); assert.equal(secondResult.agentRun.commandId, secondCommandId); assert.equal(secondResult.agentRun.traceId, secondTraceId); + assert.equal(secondResult.terminalEvidence.available, true); + assert.equal(secondResult.terminalEvidence.agentRun.commandId, secondCommandId); + assert.equal(secondResult.terminalEvidence.finalResponse.traceId, secondTraceId); assert.equal(secondResult.finalResponse.traceId, secondTraceId); assert.match(secondText, /编译成功/u); assert.doesNotMatch(secondText, /目前只有一个 HWPOD 可用/u); + assert.equal(secondText.includes('"fallback"'), false); assert.ok(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${secondCommandId}/result`)); assert.equal(ownerSessions.get("ses_issue955_historical").session.traceResults[secondTraceId].agentRun.commandId, secondCommandId); assert.equal(ownerSessions.get("ses_issue955_historical").session.traceResults[secondTraceId].finalResponse.text, secondFinalText); @@ -2109,10 +2113,14 @@ test("cloud api result polling repairs polluted completed AgentRun memory cache assert.equal(body.status, "completed"); assert.equal(body.agentRun.commandId, secondCommandId); assert.equal(body.agentRun.traceId, secondTraceId); + assert.equal(body.terminalEvidence.available, true); + assert.equal(body.terminalEvidence.agentRun.commandId, secondCommandId); + assert.equal(body.terminalEvidence.finalResponse.traceId, secondTraceId); assert.equal(body.reply.content, secondFinalText); assert.match(text, /编译成功/u); assert.match(text, /Keil MDK/u); assert.doesNotMatch(text, /目前只有一个 HWPOD 可用/u); + assert.equal(text.includes('"fallback"'), false); assert.ok(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands`)); assert.ok(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${secondCommandId}/result`)); assert.equal(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${firstCommandId}/result`), false); diff --git a/internal/cloud/server-code-agent-http.ts b/internal/cloud/server-code-agent-http.ts index 65ac0a93..fb25f6ec 100644 --- a/internal/cloud/server-code-agent-http.ts +++ b/internal/cloud/server-code-agent-http.ts @@ -2088,14 +2088,30 @@ export function createCodeAgentChatResultStore({ maxResults = 256 } = {}) { function compactCodeAgentChatResultPayload(payload, options = {}) { if (!payload || typeof payload !== "object") return payload; const limit = resultTraceEventLimit(options); + const terminalEvidence = agentRunTerminalTraceEvidence(payload, payload.traceId); return { ...payload, + ...(terminalEvidence ? { terminalEvidence: terminalEvidencePayload(terminalEvidence) } : {}), ...(payload.runnerTrace && typeof payload.runnerTrace === "object" ? { runnerTrace: compactRunnerTraceForResult(payload.runnerTrace, limit) } : {}) }; } +function terminalEvidencePayload(evidence) { + return { + available: true, + source: evidence.source, + conversationId: evidence.conversationId, + sessionId: evidence.sessionId, + threadId: evidence.threadId, + agentRun: evidence.agentRun, + traceSummary: evidence.traceSummary, + finalResponse: evidence.finalResponse, + valuesPrinted: false + }; +} + function compactRunnerTraceForResult(runnerTrace, limit = DEFAULT_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT) { if (!runnerTrace || typeof runnerTrace !== "object") return runnerTrace; const events = Array.isArray(runnerTrace.events) ? runnerTrace.events : [];