diff --git a/internal/cloud/server-code-agent-http.ts b/internal/cloud/server-code-agent-http.ts index 65f49809..36aa61a1 100644 --- a/internal/cloud/server-code-agent-http.ts +++ b/internal/cloud/server-code-agent-http.ts @@ -1376,7 +1376,8 @@ async function runAgentRunProjectionResumePass({ options = {}, traceStore = defa try { const result = await resumeAgentRunProjectionCandidate({ candidate, options, traceStore }); resumed += 1; - if (agentRunProjectionObservedTerminal(result, result?.runnerTrace ?? traceStore?.snapshot?.(candidate.traceId))) { + const currentTraceSnapshot = safeTraceStoreSnapshot(traceStore, candidate.traceId); + if (agentRunProjectionObservedTerminal(result, result?.runnerTrace ?? currentTraceSnapshot) || traceSnapshotIsTerminal(currentTraceSnapshot)) { terminal += 1; candidateRetryState.delete(retryKey); } else { @@ -1653,6 +1654,7 @@ function projectionResumeCandidateRetryDelayMs(retryAttempt, initialDelayMs, max function appendProjectionResumeError(traceStore, candidate, error, retry = {}) { const traceId = safeTraceId(candidate?.traceId); if (!traceId) return; + if (traceSnapshotIsTerminal(safeTraceStoreSnapshot(traceStore, traceId))) return; traceStore.append(traceId, { type: "turn-status", status: "degraded", @@ -1676,6 +1678,7 @@ function appendProjectionResumeError(traceStore, candidate, error, retry = {}) { function appendProjectionResumeRetry(traceStore, candidate, retry = {}) { const traceId = safeTraceId(candidate?.traceId); if (!traceId) return; + if (traceSnapshotIsTerminal(safeTraceStoreSnapshot(traceStore, traceId))) return; traceStore.append(traceId, { type: "turn-status", status: "degraded", @@ -1697,6 +1700,23 @@ function appendProjectionResumeRetry(traceStore, candidate, retry = {}) { }); } +function safeTraceStoreSnapshot(traceStore, traceId) { + try { + return typeof traceStore?.snapshot === "function" ? traceStore.snapshot(traceId) : null; + } catch { + return null; + } +} + +function traceSnapshotIsTerminal(snapshot) { + if (!snapshot || typeof snapshot !== "object") return false; + if (snapshot.finishedAt) return true; + if (isTraceCommandTerminalStatus(snapshot.status)) return true; + if (isTraceCommandTerminalStatus(snapshot.lastEvent?.status) || snapshot.lastEvent?.terminal === true) return true; + const events = Array.isArray(snapshot.events) ? snapshot.events : []; + return events.some((event) => event?.terminal === true || isTraceCommandTerminalStatus(event?.status)); +} + function logAgentRunProjectionResume(logger, payload) { try { const line = JSON.stringify(payload);