diff --git a/changelog.d/fixes/9142-fix.plan.md b/changelog.d/fixes/9142-fix.plan.md new file mode 100644 index 0000000000..aecf251f9e --- /dev/null +++ b/changelog.d/fixes/9142-fix.plan.md @@ -0,0 +1 @@ +- fix(background): detect Anthropic top-level system prompts for background task detection (#9142) diff --git a/open-sse/services/backgroundTaskDetector.ts b/open-sse/services/backgroundTaskDetector.ts index 20ac799c5d..8cbcbd3e9e 100644 --- a/open-sse/services/backgroundTaskDetector.ts +++ b/open-sse/services/backgroundTaskDetector.ts @@ -190,15 +190,31 @@ export function getBackgroundTaskReason( const messages = toMessageArray(typedBody.messages ?? typedBody.input ?? []); if (!Array.isArray(messages) || messages.length === 0) return null; - // Find system message + // Derive system content from messages array (OpenAI format) or top-level + // system field (Anthropic format). const systemMsg = messages.find( (message: BackgroundMessage) => message.role === "system" || message.role === "developer" ); - if (!systemMsg) return null; - - const systemContent = - typeof systemMsg.content === "string" ? systemMsg.content.toLowerCase() : ""; - + let systemContent = ""; + if (systemMsg && typeof systemMsg.content === "string") { + systemContent = systemMsg.content.toLowerCase(); + } else if (!systemMsg) { + // Anthropic top-level system field: string or array of text blocks + const raw = (typedBody as Record).system; + if (typeof raw === "string") { + systemContent = raw.toLowerCase(); + } else if (Array.isArray(raw)) { + systemContent = raw + .map((part) => + part && typeof (part as { text?: unknown }).text === "string" + ? (part as { text: string }).text + : "" + ) + .filter(Boolean) + .join(" ") + .toLowerCase(); + } + } if (!systemContent) return null; // Check against detection patterns diff --git a/tests/unit/triage-bugs-2026-08-02.test.ts b/tests/unit/triage-bugs-2026-08-02.test.ts index 88a7cbb300..d27536dba9 100644 --- a/tests/unit/triage-bugs-2026-08-02.test.ts +++ b/tests/unit/triage-bugs-2026-08-02.test.ts @@ -42,6 +42,26 @@ test("non-GPT-5.6 models still get max downgraded to xhigh", () => { ) ); assert.equal(translated.reasoning_effort, "xhigh"); +<<<<<<< HEAD +}); + +// ───────────────────────────────────────────────────────────────────── +// PR #9142 — Anthropic top-level `system` prompts must trigger background detection +// ───────────────────────────────────────────────────────────────────── +const { getBackgroundTaskReason, setBackgroundDegradationConfig } = + await import("../../open-sse/services/backgroundTaskDetector.ts"); + +test("#9142 Anthropic top-level system prompts must trigger background detection", () => { + setBackgroundDegradationConfig({ enabled: true }); + assert.equal( + getBackgroundTaskReason({ + system: "Generate a title for this conversation", + messages: [{ role: "user", content: "hello" }], + }), + "system_prompt_pattern" + ); +}); +======= // #9140 — VS Code routes filter out built-in auto models const { isUsableChatModel } = await import( @@ -59,3 +79,4 @@ test("#9140 VS Code listing must accept built-in auto routing entries", () => { false, "operator-created combo should still be rejected" ); +>>>>>>> origin/release/v3.8.50