diff --git a/internal/cloud/server-workbench-http.test.ts b/internal/cloud/server-workbench-http.test.ts index 98bb19ec..2daf1229 100644 --- a/internal/cloud/server-workbench-http.test.ts +++ b/internal/cloud/server-workbench-http.test.ts @@ -115,6 +115,39 @@ test("workbench turn projection treats retryable provider stream disconnect as a assert.equal(completedProjection.finalResponse?.text, "retry recovered"); }); +test("workbench turn projection treats legacy agentrun-unreachable as retryable AgentRun transport interruption (#1853)", () => { + const traceId = "trc_retryable_legacy_agentrun_unreachable"; + const result = { + traceId, + status: "failed", + failureKind: "agentrun-unreachable", + willRetry: true, + agentRun: { + runId: "run_retryable_legacy_agentrun_unreachable", + commandId: "cmd_retryable_legacy_agentrun_unreachable", + terminalStatus: "failed", + failureKind: "agentrun-unreachable", + willRetry: true + }, + providerTrace: { failureKind: "agentrun-unreachable", willRetry: true } + }; + const trace = { + traceId, + status: "failed", + events: [ + { seq: 1, type: "error", status: "failed", errorCode: "agentrun-unreachable", failureKind: "agentrun-unreachable", willRetry: true, terminal: true } + ], + eventCount: 1 + }; + const projection = createWorkbenchTurnProjection({ traceId, result, trace }); + assert.equal(projection.status, "retrying"); + assert.equal(projection.running, true); + assert.equal(projection.terminal, false); + assert.equal(projection.finalResponse, null); + assert.equal(durableTraceStatus(trace.events), "retrying"); + assert.equal(traceTerminalEvidence(trace), null); +}); + test("workbench turn projection seals final response from authoritative terminal assistant trace event (#1629)", () => { const traceId = "trc_workbench_terminal_assistant_final"; const finalText = [ diff --git a/internal/cloud/workbench-turn-projection.ts b/internal/cloud/workbench-turn-projection.ts index cd30d2c6..c7098561 100644 --- a/internal/cloud/workbench-turn-projection.ts +++ b/internal/cloud/workbench-turn-projection.ts @@ -313,13 +313,13 @@ const RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS = new Set([ ]); function isRetryableAgentRunTransportFailureKind(kind) { - return RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS.has(kind); + return RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS.has(normalizedProjectionFailureKind(kind)); } function retryableProviderInterruptionRecord(value) { const record = objectValue(value); if (!record) return null; - const kind = normalizeWorkbenchStatus(record.failureKind ?? record.errorCode ?? record.code ?? record.name); + const kind = normalizedProjectionFailureKind(record.failureKind ?? record.errorCode ?? record.code ?? record.name); if ((kind === "provider-stream-disconnected" || kind === "provider-unavailable" || isRetryableAgentRunTransportFailureKind(kind)) && record.willRetry === true) { return { failureKind: kind, @@ -340,6 +340,12 @@ function retryableProviderInterruptionRecord(value) { return null; } +function normalizedProjectionFailureKind(value) { + const kind = normalizeWorkbenchStatus(value); + if (kind === "agentrun-unreachable") return "agentrun-connect-failed"; + return kind; +} + function hasTraceProjection(trace) { return Boolean(trace && trace.status !== "missing" && (normalizedEventCount(trace) > 0 || traceLastEvent(trace))); }