mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +03:00
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>