From a7b776932c481fb989add278f30d1fd3365e5f4e Mon Sep 17 00:00:00 2001 From: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 23:18:56 +0000 Subject: [PATCH] fix(agents): classify embedded provider business denials for fallback --- .../result-fallback-classifier.test.ts | 20 ++++++++++++++ .../result-fallback-classifier.ts | 27 +++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/agents/embedded-agent-runner/result-fallback-classifier.test.ts b/src/agents/embedded-agent-runner/result-fallback-classifier.test.ts index d51325a806b..d601ffd7df5 100644 --- a/src/agents/embedded-agent-runner/result-fallback-classifier.test.ts +++ b/src/agents/embedded-agent-runner/result-fallback-classifier.test.ts @@ -89,4 +89,24 @@ describe("classifyEmbeddedAgentRunResultForModelFallback", () => { expect(result).toBeNull(); }); + + it("does not retry non-business transport error payloads", () => { + const result = classifyEmbeddedAgentRunResultForModelFallback({ + provider: "custom", + model: "llama-3.1", + result: { + payloads: [ + { + isError: true, + text: "HTTP 500: internal server error", + }, + ], + meta: { + durationMs: 42, + }, + }, + }); + + expect(result).toBeNull(); + }); }); diff --git a/src/agents/embedded-agent-runner/result-fallback-classifier.ts b/src/agents/embedded-agent-runner/result-fallback-classifier.ts index 39f200c099b..560097b668b 100644 --- a/src/agents/embedded-agent-runner/result-fallback-classifier.ts +++ b/src/agents/embedded-agent-runner/result-fallback-classifier.ts @@ -1,5 +1,10 @@ import { isSilentReplyPayloadText } from "../../auto-reply/tokens.js"; -import { classifyFailoverReason } from "../embedded-agent-helpers.js"; +import { + isAuthErrorMessage, + isAuthPermanentErrorMessage, + isBillingErrorMessage, +} from "../embedded-agent-helpers/failover-matches.js"; +import type { FailoverReason } from "../embedded-agent-helpers/types.js"; import { isGpt5ModelId } from "../gpt5-prompt-overlay.js"; import type { ModelFallbackResultClassification } from "../model-fallback.js"; import { hasOutboundDeliveryEvidence, hasVisibleAgentPayload } from "./delivery-evidence.js"; @@ -56,6 +61,24 @@ function classifyHarnessResult(params: { } } +function classifyBusinessDenialErrorPayloadReason( + errorText: string, +): Extract | null { + if (!errorText.trim()) { + return null; + } + if (isBillingErrorMessage(errorText)) { + return "billing"; + } + if (isAuthPermanentErrorMessage(errorText)) { + return "auth_permanent"; + } + if (isAuthErrorMessage(errorText)) { + return "auth"; + } + return null; +} + export function classifyEmbeddedAgentRunResultForModelFallback(params: { provider: string; model: string; @@ -105,7 +128,7 @@ export function classifyEmbeddedAgentRunResultForModelFallback(params: { code: "incomplete_result", }; } - const failoverReason = classifyFailoverReason(errorText, { provider: params.provider }); + const failoverReason = classifyBusinessDenialErrorPayloadReason(errorText); if (failoverReason) { return { message: `${params.provider}/${params.model} ended with a provider error: ${errorText}`,