Files
OmniRoute/config
Diego Rodrigues de Sa e Souza 52b26c88c9 fix(sse): sanitize empty-signature thinking blocks + hoist strict-provider system messages (#7583)
* fix(sse): sanitize empty-signature thinking blocks + hoist strict-provider system messages (#6953, #7293)

#6953: prepareClaudeRequest's "preserve latest-assistant thinking verbatim"
guard (claudeHelper.ts, anti-400 for legitimate Anthropic replay) did not
distinguish a genuine Claude signature from an empty one fabricated by a
non-Anthropic leg (e.g. codex reasoning_content). It forwarded signature:""
verbatim to Anthropic, which always 400s ("Invalid signature in thinking
block"), permanently locking combo routing onto the non-Anthropic leg. The
response-side half of this bug (openai-to-claude.ts synthesizing the empty
signature in the first place) was already fixed by #6982/PR#6982; this PR
closes the remaining request-side half. Fix: the verbatim-preserve guard now
requires every thinking-ish block on the latest assistant message to carry a
non-empty signature/data; otherwise it falls through to the existing
sanitization path (redacted_thinking + DEFAULT_THINKING_CLAUDE_SIGNATURE)
already applied to older turns.

#7293: translateRequest() is the single outbound choke point every chat
request passes through, including same-format (OpenAI→OpenAI) passthrough
where none of the format-specific translators run. systemMessageMustBeFirst()
/ PROVIDERS_SYSTEM_MUST_BE_FIRST (src/lib/memory/injection.ts, #6135/PR#6225)
was only consulted by the memory injector, so a client-injected system
message landing mid-array (OpenCode/Kilo Code style clients, Discussion
#6129) reached strict providers (xiaomi-mimo) untouched and 400'd. Fix: a new
helper (open-sse/translator/helpers/strictSystemHoist.ts) hoists every system
message onto index 0 for strict providers, reusing systemMessageMustBeFirst()
as the single source of truth, merging (never dropping) multiple offenders in
original order, and no-op'ing (same array reference) for non-strict providers
and already-compliant requests to preserve prompt-cache prefix stability.

Both defects live in the same file cluster (openai-to-claude request-path
translator + its helpers), hence one PR for both issues per triage guidance.

Regression tests:
- tests/unit/repro-6953.test.ts — RED (actual signature:'' forwarded) → GREEN
- tests/unit/probe-7293-strict-system-hoist.test.ts — RED (system message
  left at index 10 of 70) → GREEN, plus multi-offender merge, existing-leading
  merge, non-strict-provider no-op, and already-compliant no-op cases.

Gates run: file-size, complexity, cognitive-complexity (both at/under
baseline), typecheck:core (clean), eslint on changed files (clean),
test:vitest (254/254 green), plus all directly relevant existing suites
(translator-claude-helper-thinking, translator-xiaomi-mimo-reasoning-replay,
memory-system-first-6135, dashscope-cache-control-openai-2069,
xiaomi-mimo-provider, role-normalizer, translation.golden,
translators.property, translator-helper-branches, translator-claude-to-openai,
translator-same-format-null-flush — all green).

Closes #6953
Closes #7293

* chore(quality): prune the now-stale claudeHelper no-explicit-any suppression (#6953)

The #6953 fix removed the single `any` that config/quality/eslint-suppressions.json
still had frozen for open-sse/translator/helpers/claudeHelper.ts, so the entry became
stale and ESLint's stale-suppression enforcement failed the 'No new ESLint warnings'
gate — the gate went red because the code got better.

Pruned that one entry only (never a global --prune-suppressions: other entries are
other sessions' frozen debt).
2026-07-17 07:04:49 -03:00
..
2026-07-13 09:12:40 -03:00