Files
JasonBroderick a3ca33fa64 fix(sse): hoist leading text system messages on the Claude mid-conversation-system passthrough (#13072)
On the Claude Code semantic passthrough with a 1M-context model (system + tools
present), system-role messages are deliberately kept inside messages[] and only
directive-only messages (content: [] + output_config) are relocated off
messages[0]. Anthropic also rejects a text-bearing system message at messages[0]:

    messages.0: use the top-level 'system' parameter for the initial system
    prompt; the directive-only form (content: [] with output_config) is
    accepted at any position

That is exactly where the Output Styles injection lands
(open-sse/services/compression/outputStyles/apply.ts unshifts a system message),
so every Claude Code turn with an output style active on such a model fails
with 400.

Add hoistLeadingTextSystemMessages(): move only the leading run of text-bearing
system-role messages (everything before the first user/assistant turn) into the
top-level system parameter, and call it before relocateDirectiveOnlyMessages()
on that path. Genuine mid-conversation system turns keep their position and
cache prefix; directive-only messages in the run are left for the existing
relocation. Unit tests cover the injected-style case, the string top-level
system case, mixed directive/text runs, and the no-op case.

Repro: POST /v1/messages with Claude Code client headers (user-agent
claude-cli/..., x-app: cli), model claude-opus-5, a top-level system, one tool,
and messages[0] = {role: "system", content: "[OmniRoute Output Styles] ..."}.
Before: 400 from Anthropic. After: 200.

Co-authored-by: ai-stack <ops@ai-stack.local>
2026-09-09 20:59:05 -03:00
..
2026-07-29 15:18:55 -03:00
2026-05-23 01:46:59 -03:00
2026-07-13 09:12:40 -03:00
2026-08-26 14:25:01 -03:00
2026-05-04 01:36:53 -03:00
2026-07-06 02:25:17 -03:00
2026-07-29 15:18:55 -03:00
2026-08-26 14:25:01 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-13 17:27:40 -03:00