fix: keep code agent turn reads projection-only (#1594)

This commit is contained in:
Lyon
2026-06-19 12:06:58 +08:00
committed by GitHub
parent 0169125f8f
commit b780503e78
2 changed files with 13 additions and 81 deletions
+8 -9
View File
@@ -2308,7 +2308,7 @@ test("cloud api running trace reports current projection without AgentRun event
}
});
test("cloud api turn status backfills terminal AgentRun projection on read (#1555)", async () => {
test("cloud api turn status does not backfill terminal AgentRun projection on GET (#1585)", async () => {
const calls = [];
const traceId = "trc_issue1422_turn_status_fast_path";
const runId = "run_issue1422_turn_status_fast_path";
@@ -2383,14 +2383,13 @@ test("cloud api turn status backfills terminal AgentRun projection on read (#155
});
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.status, "completed");
assert.equal(body.terminal, true);
assert.equal(body.agentRun.runStatus, "claimed");
assert.equal(body.agentRun.commandState, "completed");
assert.equal(body.agentRun.terminalStatus, "completed");
assert.match(body.finalResponse.text, /issue 1555 read-side backfill/u);
assert.equal(calls.filter((call) => call.path === `/api/v1/runs/${runId}/events`).length, 1);
assert.equal(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${commandId}/result`), true);
assert.equal(body.status, "running");
assert.equal(body.terminal, false);
assert.equal(body.projectionStatus, "projecting");
assert.equal(body.agentRun.commandState, "running");
assert.equal(body.agentRun.terminalStatus ?? null, null);
assert.equal(body.finalResponse, null);
assert.deepEqual(calls, []);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
+5 -72
View File
@@ -9,7 +9,6 @@ import {
cancelAgentRunChatTurn,
codeAgentAgentRunAdapterEnabled,
initialAgentRunChatResult,
agentRunTerminalResultRefreshSatisfied,
loadPersistedAgentRunResult,
steerAgentRunChatTurn,
submitAgentRunChatTurn,
@@ -1242,68 +1241,6 @@ function codeAgentTurnStatusRefreshOptions(options = {}) {
};
}
async function refreshAgentRunProjectionForCompatRead({ traceId, result = null, trace = null, options = {} } = {}) {
if (!safeTraceId(traceId) || !shouldRefreshAgentRunProjectionOnRead(result)) return { result, runnerTrace: trace, refreshError: null };
const traceStore = options.traceStore ?? defaultCodeAgentTraceStore;
const refreshOptions = codeAgentTurnStatusRefreshOptions(options);
const performanceStore = options.backendPerformanceStore ?? options.backendPerformance;
const startedAt = nowMs();
performanceStore?.recordWorkbenchProjectorCandidate?.({ status: "seen", reason: "read_side_refresh" });
try {
const synced = await syncAgentRunChatResult({
traceId,
currentResult: result,
options: refreshOptions,
traceStore,
forceResultSync: agentRunProjectionObservedTerminal(result, trace)
});
const payload = synced.result ?? result;
if (payload && isTraceCommandTerminalStatus(payload.status)) {
await recordCodeAgentTerminalTurnStatusEffects({
payload,
params: codeAgentReadSideTerminalParams(payload, options),
options: refreshOptions
});
}
performanceStore?.recordWorkbenchProjectorBatch?.({ phase: "turn_finalize", status: "ok", durationMs: nowMs() - startedAt });
return { result: payload, runnerTrace: synced.runnerTrace ?? traceStore.snapshot(traceId), refreshError: null };
} catch (error) {
performanceStore?.recordWorkbenchProjectorBatch?.({ phase: "turn_finalize", status: error?.code === "agentrun_timeout" ? "timeout" : "error", durationMs: nowMs() - startedAt });
performanceStore?.recordWorkbenchProjectorError?.({ reason: error?.code ?? "agentrun_read_side_sync_failed" });
traceStore.append(traceId, {
type: "projection_refresh",
status: "degraded",
label: "agentrun:read_side_sync:failed",
errorCode: error?.code ?? "agentrun_read_side_sync_failed",
message: error?.message ?? "AgentRun read-side projection refresh failed",
terminal: false,
valuesPrinted: false
});
return { result, runnerTrace: traceStore.snapshot(traceId), refreshError: error };
}
}
function shouldRefreshAgentRunProjectionOnRead(result = {}) {
const agentRun = result?.agentRun && typeof result.agentRun === "object" ? result.agentRun : null;
if (!agentRun?.runId || !agentRun?.commandId) return false;
if (isTraceCommandTerminalStatus(result?.status) && agentRunTerminalResultRefreshSatisfied(result, result?.traceId)) return false;
return true;
}
function codeAgentReadSideTerminalParams(payload = {}, options = {}) {
const session = payload.session && typeof payload.session === "object" ? payload.session : null;
const sessionReuse = payload.sessionReuse && typeof payload.sessionReuse === "object" ? payload.sessionReuse : null;
return {
traceId: payload.traceId,
conversationId: safeConversationId(payload.conversationId ?? session?.conversationId ?? sessionReuse?.conversationId) || null,
sessionId: safeSessionId(payload.sessionId ?? session?.sessionId ?? sessionReuse?.sessionId) || null,
threadId: safeOpaqueId(payload.threadId ?? session?.threadId ?? sessionReuse?.threadId) || null,
projectId: payload.projectId ?? null,
ownerUserId: payload.ownerUserId ?? options.actor?.id,
ownerRole: payload.ownerRole ?? options.actor?.role
};
}
function codeAgentRequestScopedOptions(options = {}) {
return {
...options,
@@ -1341,16 +1278,10 @@ async function readCodeAgentCompatProjection(traceId, options = {}) {
if (result?.ownerUserId && !readModel.canReadOwner(result.ownerUserId)) return forbiddenTurnSnapshot(traceId);
const projectionTrace = await readModel.traceSnapshot(traceId);
let trace = traceSnapshotWithTerminalEvidence(projectionTrace, result, traceId, null);
const refreshed = await refreshAgentRunProjectionForCompatRead({ traceId, result, trace, options });
if (refreshed.runnerTrace) trace = traceSnapshotWithTerminalEvidence(refreshed.runnerTrace, result, traceId, refreshed.refreshError ?? null);
if (refreshed.result && refreshed.result !== result) {
result = refreshed.result;
trace = traceSnapshotWithTerminalEvidence(refreshed.runnerTrace ?? trace, result, traceId, refreshed.refreshError ?? null);
}
const projection = readModel.projectionDiagnostics({ traceId, result, trace, refreshError: refreshed.refreshError ?? null });
recordCodeAgentProjectionMetrics(options, { result, trace, projection, refreshError: refreshed.refreshError ?? null });
const projection = readModel.projectionDiagnostics({ traceId, result, trace, refreshError: null });
recordCodeAgentProjectionMetrics(options, { result, trace, projection, refreshError: null });
const found = Boolean(result || session || (trace && trace.status !== "missing") || trace?.persisted === true);
return { result, session, trace, projection, found, refreshError: refreshed.refreshError ?? null };
return { result, session, trace, projection, found, refreshError: null };
}
function projectedSessionTraceResult(session, traceId) {
@@ -2736,6 +2667,8 @@ function traceSnapshotWithTerminalEvidence(snapshot, result, traceId, refreshErr
ok: true,
status: "expired",
traceStatus: "expired",
projectionStatus: "projecting",
projectionHealth: "degraded",
persisted: true,
retention: traceRetentionSummary("expired"),
conversationId: evidence.conversationId,