diff --git a/internal/cloud/code-agent-agentrun-adapter.ts b/internal/cloud/code-agent-agentrun-adapter.ts index c9cd5542..8e8c34e2 100644 --- a/internal/cloud/code-agent-agentrun-adapter.ts +++ b/internal/cloud/code-agent-agentrun-adapter.ts @@ -563,10 +563,25 @@ function agentRunDispatchRetryDelayMs(policy, retryAttempt) { return Math.min(cap, Math.trunc(base * Math.pow(2, Math.max(0, Number(retryAttempt ?? 1) - 1)))); } +const RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS = new Set([ + "agentrun-connect-failed", + "agentrun-proxy-exec-failed", + "agentrun-manager-fetch-failed", + "agentrun-timeout", + "timeout", + "fetch-failed", + "network-error" +]); + +function isRetryableAgentRunTransportFailureKind(failureKind) { + return RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS.has(normalizedFailureKind(failureKind)); +} + function agentRunDispatchRetryable(error, failureKind) { const status = Number(error?.statusCode ?? error?.status ?? error?.agentRunError?.statusCode ?? 0); if ([429, 502, 503, 504].includes(status)) return true; - if (["provider-unavailable", "provider-stream-disconnected", "agentrun-timeout", "agentrun-timeout", "timeout", "fetch-failed", "network-error"].includes(failureKind)) return true; + if (["provider-unavailable", "provider-stream-disconnected"].includes(failureKind)) return true; + if (isRetryableAgentRunTransportFailureKind(failureKind)) return true; return /ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|socket hang up|fetch failed|network|timeout/iu.test(String(error?.message ?? "")); } @@ -2559,11 +2574,14 @@ function isRetryableProviderInterruptionPayload(payload = {}, options = {}) { const kind = normalizedFailureKind(payload?.failureKind ?? payload?.errorCode ?? payload?.code); if (kind === "provider-stream-disconnected") return payload?.willRetry === true; if (kind === "provider-unavailable") return payload?.willRetry === true || (options.activeError === true && payload?.terminal !== true && payload?.final !== true); + if (isRetryableAgentRunTransportFailureKind(kind)) return payload?.willRetry === true; return false; } function normalizedFailureKind(value) { - return String(value ?? "").trim().toLowerCase().replace(/_/gu, "-") || null; + const normalized = String(value ?? "").trim().toLowerCase().replace(/_/gu, "-") || null; + if (normalized === "agentrun-unreachable") return "agentrun-connect-failed"; + return normalized; } function numberOrNull(value) { diff --git a/internal/cloud/workbench-turn-projection.ts b/internal/cloud/workbench-turn-projection.ts index e1a04135..24e2fe03 100644 --- a/internal/cloud/workbench-turn-projection.ts +++ b/internal/cloud/workbench-turn-projection.ts @@ -284,11 +284,22 @@ function retryableProviderInterruptionEvidence(...values) { return null; } +const RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS = new Set([ + "agentrun-connect-failed", + "agentrun-proxy-exec-failed", + "agentrun-manager-fetch-failed", + "agentrun-timeout" +]); + +function isRetryableAgentRunTransportFailureKind(kind) { + return RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS.has(kind); +} + function retryableProviderInterruptionRecord(value) { const record = objectValue(value); if (!record) return null; const kind = normalizeWorkbenchStatus(record.failureKind ?? record.errorCode ?? record.code ?? record.name); - if ((kind === "provider-stream-disconnected" || kind === "provider-unavailable") && record.willRetry === true) { + if ((kind === "provider-stream-disconnected" || kind === "provider-unavailable" || isRetryableAgentRunTransportFailureKind(kind)) && record.willRetry === true) { return { failureKind: kind, willRetry: true,