Skip to content

fix(stream): accept the buffer size glm.ts has been passing since #12179 - #12925

Merged
diegosouzapw merged 3 commits into
cheaper-inference:release/v3.8.51from
ntdatt812:fix/glm-stream-buffer-arity
Sep 10, 2026
Merged

diegosouzapw merged 3 commits into
cheaper-inference:release/v3.8.51from
ntdatt812:fix/glm-stream-buffer-arity

Conversation

@ntdatt812

Copy link
Copy Markdown
Contributor

This currently fails CI on every open PR in the repo, including ones that touch nothing near it.

#12179 gave GLM a 64 KB stream buffer by adding a 16th argument to createSSETransformStreamWithLogger:

// GLM is a high-throughput provider — use a larger stream buffer (64KB) to
// keep provider → client pacing ahead of the model's token emission rate.
const transform = createSSETransformStreamWithLogger(
  …,
  suppressThinkClose,
  undefined,
  undefined,
  65536          // ← the 16th
);

The signature takes 15. Two things followed.

The visible one. open-sse/executors/glm.ts(244,5): error TS2554: Expected 2-15 arguments, but got 16. The API Route Typecheck gate is "reject new errors not covered by the frozen baseline", so it reports 1 new/regressed TypeScript error and fails — on any PR, since nobody's branch removes it. I found it while auditing my own open PRs: #12896 fails the identical 8 checks after a clean rebase onto release/v3.8.51, with a test that passes locally, and #12919 (not mine) fails the same 8.

The quiet one. The extra argument was simply dropped, and createSSEStream hard-coded the queue budget:

{ highWaterMark: 16384 },
{ highWaterMark: 16384 }

So the 64 KB that call site asks for never reached the stream. The perf change that hunk describes has not been in effect since it landed — the comment above it has been describing something that does not happen.

The change

StreamOptions gains streamBufferBytes, defaulting to the 16384 every provider used before it was configurable; the convenience wrapper takes it as a 16th parameter and forwards it; both queues use it. glm.ts is unchanged — its existing call now compiles and takes effect, which is the smallest edit that makes the original intent true rather than deleting it.

The default is a parameter default rather than || DEFAULT, so a deliberate 0 — a queue that applies backpressure immediately — is honoured instead of silently replaced. There is a test for exactly that, because || here is the easy way to write it and the wrong one.

Verification

tsc -p tsconfig.typecheck-api.json: 299 → 298 errors. The one removed is glm.ts TS2554, the one the gate names. Nothing new appears; the remaining stream.ts TS2353 about cancel is in the frozen baseline (it sat at line 2986 before this change and 2998 after — same error, shifted by the added lines).

tests/unit/sse-stream-buffer-bytes.test.ts: 5 passed. A TransformStream's writable queue starts with desiredSize === highWaterMark, so the assertions measure the queue that was actually built rather than standing in for it.

Each claim checked by breaking it:

reverted fails
queues back to the hard-coded 16384 the three budget assertions
wrapper stops forwarding the argument only the 16-positional-argument test
default written as || DEFAULT only the streamBufferBytes: 0 test

I have not touched the buffer size for any other provider: without the option, every stream still gets 16384.

ntdatt812 and others added 3 commits September 10, 2026 18:17
…aper-inference#12179

createSSETransformStreamWithLogger, which takes 15. Two things followed.

The visible one: `open-sse/executors/glm.ts(244,5) TS2554` fails the API Route
Typecheck gate, and that gate is "reject new errors not in the frozen
baseline" -- so it fails on every open PR in the repo, not only on changes that
touch this file.

The quiet one: the argument was dropped, and createSSEStream hard-coded
`{ highWaterMark: 16384 }` on both queues. The 64 KB the call site asks for
never reached the stream, so the perf change that hunk describes has not been
in effect at all.

Adds `streamBufferBytes` to StreamOptions, defaulting to the 16384 every
provider used before it was configurable, threads it through the convenience
wrapper as a 16th parameter, and uses it for both queues. glm.ts is unchanged:
its existing call now compiles and takes effect.

The default is a parameter default, not `|| DEFAULT`, so a deliberate 0 --
a queue that applies backpressure immediately -- is honoured rather than
silently replaced.
…le exit

Two follow-ups on the rebased branch.

glm.ts now passes the budget. The tip had already cleared the TS2554 by
deleting the 16th argument, leaving a comment saying the highWaterMark stays at
the helper default — so the queue budget cheaper-inference#12179 asked for was still not applied,
and this PR's new parameter had no caller. Wiring the call site is what turns
the capability into the fix.

The test file also no longer hangs. Every stream it builds arms a 10s idle
watchdog via setInterval in createSSEStream's start, and nothing cancelled them,
so node:test kept waiting on a non-empty event loop long after the assertions
passed. Cancelling each readable in an after hook runs the TransformStream's
cancel handler, which clears the timer: the file reports in ~7s instead of
running until the runner is killed.
@diegosouzapw
diegosouzapw force-pushed the fix/glm-stream-buffer-arity branch from 5324bc0 to 6824c48 Compare September 10, 2026 21:25
@diegosouzapw
diegosouzapw merged commit af49d49 into cheaper-inference:release/v3.8.51 Sep 10, 2026
3 of 7 checks passed
This was referenced Sep 11, 2026
diegosouzapw added a commit that referenced this pull request Sep 11, 2026
Four base-reds that appeared on the tip today, none owned by any open PR:

- providers-constants-split / provider-node-reserved-prefix: GreenPT (#13024)
  and EURouter (#13025) move the totals 238 -> 240 and 408 -> 410.
- provider-translate-path-golden: same two providers, regenerated with
  UPDATE_GOLDEN=1; the snapshot delta is purely additive.
- glm-sse-transform-arity: #12925 declared the buffer-size slot GLM had been
  filling since #12179. The invariant is unchanged — the value must land in a
  real parameter, never a dropped positional — so the guard now checks it from
  the helper side and requires the named constant at the call site instead of
  a magic literal.
- injection-guard-scan-bound-3932: #13104 split the 16 KB budget between head
  and tail, so 'outside the window' is now the middle of an oversized body,
  not its end. The 16 KB ceiling these cases protect is untouched.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
diegosouzapw added a commit to initguru/OmniRoute that referenced this pull request Sep 16, 2026
…ng parameter

createSSETransformStreamWithLogger gained a new requestedThinking
parameter inserted before customToolNames. glm.ts's translateSseResponse
still called it with the pre-existing positional argument list, so the
new parameter silently absorbed the old customToolNames slot, and the
GLM_STREAM_BUFFER_BYTES tuning value (cheaper-inference#12925) landed on
requestToolIdentityMap instead of streamBufferBytes — a TS2345 (number
is not assignable to Map<...> | null) caught by
check:open-sse-typecheck, and a real loss of GLM's 64KB stream buffer
budget. Insert an explicit `undefined` for requestedThinking to restore
the original alignment.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
diegosouzapw added a commit that referenced this pull request Sep 17, 2026
…ive thinking (#12905)

* fix(thinking): recognize adaptive thinking + parse/scrub DSML tool-call markers

Two defects combined to break DeepSeek-V4-Flash turns and raise 502
empty_response on Claude Code autocompact.

Defect 1 — DSML tool-call markers leaked as visible content:
DeepSeek-V4-Flash occasionally emits tool calls in a non-standard DSML
text format using full-width pipes instead of the OpenAI tool_calls JSON.
Two shapes appear in production call logs:
  - complete block: <|DSML|:Read><path>...</path></|DSML|:Read>
  - stray closers (truncated call): </|DSML|parameter></|DSML|invoke>
    </|DSML|tool_calls>, sometimes trailing a system-prompt echo
The openai-compatible path never parsed these, so the markers leaked to
the client as visible content and the turn ended incomplete.

Fix: add open-sse/utils/dsmlToolCalls.ts — parseDsmlToolCalls() converts
complete DSML blocks into OpenAI tool_calls and strips stray closing
markers from content (streaming-safe via a holdback for partial openers).
Wire it into the response translator before extractXmlInvokeBlocks so
DSML and XML invoke tool calls share the same pending queue.

Defect 2 — adaptive thinking silently suppressed:
A prior inline === 'enabled' check on body.thinking.type silently
suppressed adaptive (the intent Claude Code actually sends), so
reasoning was dropped. The model then emitted DSML tool-call markers
as plain text, producing an incomplete stop finish. Fix: use
hasActiveClaudeThinking() (which recognizes enabled AND adaptive) to
set requestedThinking, thread it through stream.ts and translator
state, and gate thinking block emission on state.requestedThinking
so upstream reasoning_content only relays when the client opted in.

Tests: 29/29 (6 dsml-tool-calls, 5 thinking-active-claude-adapter,
3 translator-resp-dsml-integration, 15 translator-resp-openai-to-claude
incl. requestedThinking suppression regression). typecheck:core clean.

* fix(sse): strip echoed system-prompt preamble + preserve large analysis/summary blocks

DeepSeek-V4 and similar models echo the OMNIROUTE_SYSTEM_INSTRUCTION_APPEND
directive (appended to the system tail by claude-to-openai.ts) and whole chunks
of the system prompt (<analysis>/<system-reminder>/<summary> blocks, prose
reproductions of the superpowers skill section) verbatim at the START of their
reply — the 'system message leak' persisting after the request-side fix.

Add two streaming-safe preamble strippers in directivePreambleStripper.ts:
- createDirectivePreambleStripper(directive): drops a leading reproduction of
  the exact configured directive across arbitrary SSE chunk boundaries.
- createSystemPreambleStripper(): removes <analysis>/<system-reminder>/
  <summary> echo blocks and known prose heads (Phase B) from the very start
  of a stream, only while the stream is still a preamble.

Wire both into openai-to-claude.ts content-delta path: chain the exact-directive
stripper then the system-echo stripper before DSML/XML-invoke parsing, so a
leading system echo is dropped before it reaches the client.

Preserve large blocks (>= SYSTEM_ECHO_THRESHOLD=1000 chars) and blocks with no
trailing content — these are the model's real response (e.g. a Claude Code
autocompact summary), not a short system-echo. Stops the autocompact
empty-response regression where a whole-summary <analysis> block was stripped
to empty (3a8515).

Regression: origin's markdown-boundary feature (bufferedPrefix /
splitMarkdownBoundary, commit 1b39873) is preserved — preamble strip runs
before the markdown buffer rehydration, and the scrubbed content flows into
the existing DSML/XML-invoke/markdown pipeline unchanged.

TDD: tests/unit/directive-preamble-strip.test.ts (7 cases),
system-preamble-strip.test.ts (12 cases incl. 3a8515 regression),
system-preamble-wiring.test.ts (3 integration cases); group F regression
24/24 green; typecheck:core 0 errors.

* fix(sse): gate thinking block on requestedThinking + synthesize text block for reasoning-only responses

Reasoning-content (thinking) blocks were emitted unconditionally to
Claude-format clients, leaking reasoning to thinking-opt-out clients
(Claude Code sends thinking:{type:"disabled"}) — the operator reported
'reasoning is exposed'. On reasoning-only upstream responses (GLM-5.2
autocompact pattern), the unconditional thinking block also caused either
a 502 'no content block' at flush, or — after a text-block fallback — an
autocompact 'empty response' rejection that looped the session forever.

Streaming translator (openai-to-claude.ts):
- Compute hasReasoning outside the emission gate; accumulate into
  state._reasoningAccum always (so fix B can fire).
- Gate only the thinking-block EMISSION on requestedThinking === true.
- FIX B at finish: when no text block was started and requestedThinking
  !== true, synthesize a text content block from _reasoningAccum so
  autocompact can extract the summary (no 502, compact applies).
- Skip fix B when requestedThinking === true to avoid double-exposure
  (thinking block + text block both carrying reasoning).

Non-streaming translator (responseTranslator.ts):
- Thread requestedThinking through translateNonStreamingResponse into
  convertOpenAINonStreamingToClaude.
- suppressThinking = requestedThinking === false: drop the thinking block
  when content is present (no leak); relay reasoning as a text block when
  the response is reasoning-only (no 502). requestedThinking === undefined
  keeps the legacy 'always a thinking block' relay.

chatCore.ts: pass hasActiveClaudeThinking(body) to the non-stream
translate call (inline, since the shared const is in the stream branch's
temporal dead zone here).

Tests: 25/25 (5 gate-restore, 1 gate-502-repro, 4 nonstream-leak,
15 resp-openai-to-claude incl. requestedThinking suppression regression).
Group E (22) + F (14) regression-free. typecheck:core clean.

* fix(sse): restore requestToolIdentityMap in the Codex CLI responses-translation path

The needsResponsesTranslation branch (openai-responses -> openai, used
when the client also speaks Responses) silently dropped the
requestToolIdentityMap argument to createSSETransformStreamWithLogger
when the requestedThinking parameter was added, reverting the #7936
tool-identity round-trip fix for that branch. The sibling
needsTranslation branch was updated correctly; restore the same
argument here.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* docs(changelog): add the 3 fragments documented in the PR body

The PR body already writes out the changelog.d/ entries for the DSML
parser (Group F), the directive-preamble stripper (Group E), and the
reasoning-gate thinking-leak fix (Group G), but none of the files
existed in the diff. Add them so the release aggregator picks them up.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(sse): realign GLM's positional call after the new requestedThinking parameter

createSSETransformStreamWithLogger gained a new requestedThinking
parameter inserted before customToolNames. glm.ts's translateSseResponse
still called it with the pre-existing positional argument list, so the
new parameter silently absorbed the old customToolNames slot, and the
GLM_STREAM_BUFFER_BYTES tuning value (#12925) landed on
requestToolIdentityMap instead of streamBufferBytes — a TS2345 (number
is not assignable to Map<...> | null) caught by
check:open-sse-typecheck, and a real loss of GLM's 64KB stream buffer
budget. Insert an explicit `undefined` for requestedThinking to restore
the original alignment.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(sse): make the system-preamble stripper opt-in and flush it at stream end

`createSystemPreambleStripper()` was wired DEFAULT-ON and unconditional in the
openai→claude streaming translator, unlike the exact-directive stripper right
above it, which only runs when the operator configured
OMNIROUTE_SYSTEM_INSTRUCTION_APPEND. Cause: the system-echo stripper recognises
its openers by English-prose heuristics ("# Skill usage", "# Verification
Process", <analysis>/<summary>/<system-reminder>), so leaving it always-on made
it mutate the response payload of EVERY openai→claude stream. A legitimate reply
opening with "# Skill usage: how to write one\n\nHere is the guide." lost that
whole section. It is now gated on OMNIROUTE_STRIP_SYSTEM_PREAMBLE=1, mirroring
the directive stripper's opt-in.

Second cause, same feature: neither stripper was ever flushed. Both buffer while
a construct is still undecided — a directive prefix that never completes, an
<analysis> block that never closes — and nothing released that buffer at the end
of the stream. A reply consisting of an unterminated echo block therefore reached
the client as an EMPTY message: the answer was held in the buffer and discarded
with the stripper. Both strippers now expose flush(), the finish handler calls it
for both, and the released text is emitted as a text block. A construct that WAS
finally classified as an echo is not resurrected (the drop is final).

Tests: tests/unit/system-preamble-gate-and-flush.test.ts pins the default-off
contract, the opted-in behaviour, the flush for both strippers (unit + wiring),
and the no-resurrection guard. system-preamble-wiring.test.ts now opts in
explicitly, since it exercises the stripping path.

* fix(sse): thread the client's thinking intent into the non-streaming path

The streaming and non-streaming translators disagreed on the default meaning of
`requestedThinking`, so the SAME request produced different shapes depending on
`stream`. Cause: chatCore computes the client's intent
(hasActiveClaudeThinking) and threads it into the SSE translator, which relays
reasoning as a thinking block only when it is explicitly `true` — but NO caller
ever passed it to translateNonStreamingResponse(). The non-streaming
OpenAI→Claude conversion therefore only ever saw `undefined`, its legacy
"always relay a thinking block" default, and leaked reasoning to a client that
had opted out with `thinking: {"type":"disabled"}`. The streaming plumbing also
coerced an omitted value into an explicit `false`, hiding the divergence behind
two different spellings of "no intent".

Fix (least destructive of the options): do NOT flip either gate — both encode a
deliberate, regression-tested contract — but give the non-streaming path the
same input the streaming path already has. runNonStreamingProviderLeg owns the
client body (`sourceBody`), so it computes the intent with the very same helper
and passes it down through translateNonStreamingClientResponse. `undefined`
keeps its documented back-compat relay for callers that cannot express intent
(issue-7856 / issue-6623), and stream.ts no longer defaults the parameter to
`false`, so "absent" now means the same thing in both signatures.

No content is lost by the suppression: a reasoning-ONLY response is still
relayed as an ordinary text block (no empty response, no 502) — exactly what the
streaming finish handler does.

Tests: tests/unit/nonstream-requested-thinking-parity.test.ts drives the real
provider leg with thinking disabled / enabled / adaptive.

---------

Co-authored-by: Jihyun Son <jihyun.son@sk.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…aper-inference#12179 (cheaper-inference#12925)

Rebased onto the tip and completed, per the maintainer's call to finish the wiring rather than merge the capability alone.

What changed since your version:

The tip had already cleared the TS2554 by deleting the 16th argument, leaving a comment that the highWaterMark stays at the helper default. So the base-red you found is gone, but the 64 KB cheaper-inference#12179 asked for was still not applied and your new parameter had no caller. glm.ts now passes it, which is what turns the capability into the fix.

Your test file also hung the runner: every stream createSSEStream builds arms a 10s idle watchdog via setInterval in start, and nothing cancelled them, so node:test waited on a non-empty event loop long after the assertions passed. Cancelling each readable in an after hook runs the cancel handler that clears the timer — the file now reports in about 7 seconds. Worth knowing for future stream tests.

Your five assertions are unchanged and all pass. Reading the writable's desiredSize to measure the queue budget the stream was actually built with, rather than standing in for it, is the detail that makes this testable at all — and the 0-budget case pinning `??` against `||` is the kind of thing that silently rots otherwise.

Thank you also for separating your own red checks from the base's and reporting what you found there. That is how cheaper-inference#12919's identical failures got explained instead of chased.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ive thinking (cheaper-inference#12905)

* fix(thinking): recognize adaptive thinking + parse/scrub DSML tool-call markers

Two defects combined to break DeepSeek-V4-Flash turns and raise 502
empty_response on Claude Code autocompact.

Defect 1 — DSML tool-call markers leaked as visible content:
DeepSeek-V4-Flash occasionally emits tool calls in a non-standard DSML
text format using full-width pipes instead of the OpenAI tool_calls JSON.
Two shapes appear in production call logs:
  - complete block: <|DSML|:Read><path>...</path></|DSML|:Read>
  - stray closers (truncated call): </|DSML|parameter></|DSML|invoke>
    </|DSML|tool_calls>, sometimes trailing a system-prompt echo
The openai-compatible path never parsed these, so the markers leaked to
the client as visible content and the turn ended incomplete.

Fix: add open-sse/utils/dsmlToolCalls.ts — parseDsmlToolCalls() converts
complete DSML blocks into OpenAI tool_calls and strips stray closing
markers from content (streaming-safe via a holdback for partial openers).
Wire it into the response translator before extractXmlInvokeBlocks so
DSML and XML invoke tool calls share the same pending queue.

Defect 2 — adaptive thinking silently suppressed:
A prior inline === 'enabled' check on body.thinking.type silently
suppressed adaptive (the intent Claude Code actually sends), so
reasoning was dropped. The model then emitted DSML tool-call markers
as plain text, producing an incomplete stop finish. Fix: use
hasActiveClaudeThinking() (which recognizes enabled AND adaptive) to
set requestedThinking, thread it through stream.ts and translator
state, and gate thinking block emission on state.requestedThinking
so upstream reasoning_content only relays when the client opted in.

Tests: 29/29 (6 dsml-tool-calls, 5 thinking-active-claude-adapter,
3 translator-resp-dsml-integration, 15 translator-resp-openai-to-claude
incl. requestedThinking suppression regression). typecheck:core clean.

* fix(sse): strip echoed system-prompt preamble + preserve large analysis/summary blocks

DeepSeek-V4 and similar models echo the OMNIROUTE_SYSTEM_INSTRUCTION_APPEND
directive (appended to the system tail by claude-to-openai.ts) and whole chunks
of the system prompt (<analysis>/<system-reminder>/<summary> blocks, prose
reproductions of the superpowers skill section) verbatim at the START of their
reply — the 'system message leak' persisting after the request-side fix.

Add two streaming-safe preamble strippers in directivePreambleStripper.ts:
- createDirectivePreambleStripper(directive): drops a leading reproduction of
  the exact configured directive across arbitrary SSE chunk boundaries.
- createSystemPreambleStripper(): removes <analysis>/<system-reminder>/
  <summary> echo blocks and known prose heads (Phase B) from the very start
  of a stream, only while the stream is still a preamble.

Wire both into openai-to-claude.ts content-delta path: chain the exact-directive
stripper then the system-echo stripper before DSML/XML-invoke parsing, so a
leading system echo is dropped before it reaches the client.

Preserve large blocks (>= SYSTEM_ECHO_THRESHOLD=1000 chars) and blocks with no
trailing content — these are the model's real response (e.g. a Claude Code
autocompact summary), not a short system-echo. Stops the autocompact
empty-response regression where a whole-summary <analysis> block was stripped
to empty (3a8515).

Regression: origin's markdown-boundary feature (bufferedPrefix /
splitMarkdownBoundary, commit f03d99a) is preserved — preamble strip runs
before the markdown buffer rehydration, and the scrubbed content flows into
the existing DSML/XML-invoke/markdown pipeline unchanged.

TDD: tests/unit/directive-preamble-strip.test.ts (7 cases),
system-preamble-strip.test.ts (12 cases incl. 3a8515 regression),
system-preamble-wiring.test.ts (3 integration cases); group F regression
24/24 green; typecheck:core 0 errors.

* fix(sse): gate thinking block on requestedThinking + synthesize text block for reasoning-only responses

Reasoning-content (thinking) blocks were emitted unconditionally to
Claude-format clients, leaking reasoning to thinking-opt-out clients
(Claude Code sends thinking:{type:"disabled"}) — the operator reported
'reasoning is exposed'. On reasoning-only upstream responses (GLM-5.2
autocompact pattern), the unconditional thinking block also caused either
a 502 'no content block' at flush, or — after a text-block fallback — an
autocompact 'empty response' rejection that looped the session forever.

Streaming translator (openai-to-claude.ts):
- Compute hasReasoning outside the emission gate; accumulate into
  state._reasoningAccum always (so fix B can fire).
- Gate only the thinking-block EMISSION on requestedThinking === true.
- FIX B at finish: when no text block was started and requestedThinking
  !== true, synthesize a text content block from _reasoningAccum so
  autocompact can extract the summary (no 502, compact applies).
- Skip fix B when requestedThinking === true to avoid double-exposure
  (thinking block + text block both carrying reasoning).

Non-streaming translator (responseTranslator.ts):
- Thread requestedThinking through translateNonStreamingResponse into
  convertOpenAINonStreamingToClaude.
- suppressThinking = requestedThinking === false: drop the thinking block
  when content is present (no leak); relay reasoning as a text block when
  the response is reasoning-only (no 502). requestedThinking === undefined
  keeps the legacy 'always a thinking block' relay.

chatCore.ts: pass hasActiveClaudeThinking(body) to the non-stream
translate call (inline, since the shared const is in the stream branch's
temporal dead zone here).

Tests: 25/25 (5 gate-restore, 1 gate-502-repro, 4 nonstream-leak,
15 resp-openai-to-claude incl. requestedThinking suppression regression).
Group E (22) + F (14) regression-free. typecheck:core clean.

* fix(sse): restore requestToolIdentityMap in the Codex CLI responses-translation path

The needsResponsesTranslation branch (openai-responses -> openai, used
when the client also speaks Responses) silently dropped the
requestToolIdentityMap argument to createSSETransformStreamWithLogger
when the requestedThinking parameter was added, reverting the cheaper-inference#7936
tool-identity round-trip fix for that branch. The sibling
needsTranslation branch was updated correctly; restore the same
argument here.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* docs(changelog): add the 3 fragments documented in the PR body

The PR body already writes out the changelog.d/ entries for the DSML
parser (Group F), the directive-preamble stripper (Group E), and the
reasoning-gate thinking-leak fix (Group G), but none of the files
existed in the diff. Add them so the release aggregator picks them up.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(sse): realign GLM's positional call after the new requestedThinking parameter

createSSETransformStreamWithLogger gained a new requestedThinking
parameter inserted before customToolNames. glm.ts's translateSseResponse
still called it with the pre-existing positional argument list, so the
new parameter silently absorbed the old customToolNames slot, and the
GLM_STREAM_BUFFER_BYTES tuning value (cheaper-inference#12925) landed on
requestToolIdentityMap instead of streamBufferBytes — a TS2345 (number
is not assignable to Map<...> | null) caught by
check:open-sse-typecheck, and a real loss of GLM's 64KB stream buffer
budget. Insert an explicit `undefined` for requestedThinking to restore
the original alignment.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(sse): make the system-preamble stripper opt-in and flush it at stream end

`createSystemPreambleStripper()` was wired DEFAULT-ON and unconditional in the
openai→claude streaming translator, unlike the exact-directive stripper right
above it, which only runs when the operator configured
OMNIROUTE_SYSTEM_INSTRUCTION_APPEND. Cause: the system-echo stripper recognises
its openers by English-prose heuristics ("# Skill usage", "# Verification
Process", <analysis>/<summary>/<system-reminder>), so leaving it always-on made
it mutate the response payload of EVERY openai→claude stream. A legitimate reply
opening with "# Skill usage: how to write one\n\nHere is the guide." lost that
whole section. It is now gated on OMNIROUTE_STRIP_SYSTEM_PREAMBLE=1, mirroring
the directive stripper's opt-in.

Second cause, same feature: neither stripper was ever flushed. Both buffer while
a construct is still undecided — a directive prefix that never completes, an
<analysis> block that never closes — and nothing released that buffer at the end
of the stream. A reply consisting of an unterminated echo block therefore reached
the client as an EMPTY message: the answer was held in the buffer and discarded
with the stripper. Both strippers now expose flush(), the finish handler calls it
for both, and the released text is emitted as a text block. A construct that WAS
finally classified as an echo is not resurrected (the drop is final).

Tests: tests/unit/system-preamble-gate-and-flush.test.ts pins the default-off
contract, the opted-in behaviour, the flush for both strippers (unit + wiring),
and the no-resurrection guard. system-preamble-wiring.test.ts now opts in
explicitly, since it exercises the stripping path.

* fix(sse): thread the client's thinking intent into the non-streaming path

The streaming and non-streaming translators disagreed on the default meaning of
`requestedThinking`, so the SAME request produced different shapes depending on
`stream`. Cause: chatCore computes the client's intent
(hasActiveClaudeThinking) and threads it into the SSE translator, which relays
reasoning as a thinking block only when it is explicitly `true` — but NO caller
ever passed it to translateNonStreamingResponse(). The non-streaming
OpenAI→Claude conversion therefore only ever saw `undefined`, its legacy
"always relay a thinking block" default, and leaked reasoning to a client that
had opted out with `thinking: {"type":"disabled"}`. The streaming plumbing also
coerced an omitted value into an explicit `false`, hiding the divergence behind
two different spellings of "no intent".

Fix (least destructive of the options): do NOT flip either gate — both encode a
deliberate, regression-tested contract — but give the non-streaming path the
same input the streaming path already has. runNonStreamingProviderLeg owns the
client body (`sourceBody`), so it computes the intent with the very same helper
and passes it down through translateNonStreamingClientResponse. `undefined`
keeps its documented back-compat relay for callers that cannot express intent
(issue-7856 / issue-6623), and stream.ts no longer defaults the parameter to
`false`, so "absent" now means the same thing in both signatures.

No content is lost by the suppression: a reasoning-ONLY response is still
relayed as an ordinary text block (no empty response, no 502) — exactly what the
streaming finish handler does.

Tests: tests/unit/nonstream-requested-thinking-parity.test.ts drives the real
provider leg with thinking disabled / enabled / adaptive.

---------

Co-authored-by: Jihyun Son <jihyun.son@sk.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
AndresDiagoM added a commit to AndresDiagoM/OmniRoute that referenced this pull request Oct 19, 2026
…helper

clientTools now sits before streamBufferBytes, so the trailing buffer-size slot GLM fills (cheaper-inference#12925) stays last and GLM's call matches the helper arity. GLM passes null for clientTools; the translate call in streamingTail drops its placeholder undefined.
diegosouzapw added a commit that referenced this pull request Oct 22, 2026
…ng from every provider (#17118) (#17119)

* fix(sse): hand Anthropic-format clients their declared tool-name casing from every provider

Claude-format responses assembled by openai-to-claude, gemini-to-claude and the
non-streaming responseTranslator all ended in restoreClaudeToolName(), whose
canonical upgrade (read -> Read, #7926/#9008/#10392/#11085) assumes the client
is Claude Code. OpenCode's @ai-sdk/anthropic provider declares lowercase tools,
so a combo falling back from native claude to Antigravity, Codex or Gemini (or
any of those used directly over /v1/messages) answered with tool_use.name
"Read" for a tool declared as "read", and every tool call failed client-side
("No tool named Read").

Lift the #12721 passthrough rule into a shared module and apply it on the
translate paths too: alias ledger first, then the CLIENT request's declared
spelling (exact, then case-insensitive), then the legacy canonical upgrade only
for names the request never declared. Claude Code declares "Read", so it keeps
getting "Read" back when an OpenAI-style upstream downcases it; OpenCode gets
"read" back from the same upstream. The client's tools[] is threaded into the
translate-mode stream state and into translateNonStreamingResponse.

* docs(changelog): add fragment for #17119

* fix(sse): keep streamBufferBytes the last parameter of the transform helper

clientTools now sits before streamBufferBytes, so the trailing buffer-size slot GLM fills (#12925) stays last and GLM's call matches the helper arity. GLM passes null for clientTools; the translate call in streamingTail drops its placeholder undefined.

* fix(sse): carry declared tools through Claude passthrough (#17118)

* refactor(sse): split declaredToolCasing helpers under the complexity gate

findDeclaredToolName (cyclomatic 17, cognitive 18) and restoreDeclaredToolName
(cognitive 17) were over the new-code limit of 15. Extract declaredNameOf()
for the per-entry tools[] spelling and aliasLedgerName() for the _toolNameMap
precedence; behaviour is unchanged and the 17118/17119 suites still pass.

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
DarknessKiller added a commit to DarknessKiller/OmniRoute that referenced this pull request Oct 25, 2026
…1f05`

- New UI Changes are not applied

Squashed commit of the following:

commit fb2808905d16c0ed51ec54ee206f798465001f05
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:02:31 2026 -0300

    fix(cli): resolve the systray entry and unwrap its constructor (#17328)

    Resolve the systray runtime entry and unwrap its constructor in the CLI tray.

    Evidence: focused tests pass (tests/unit/cli/tray-failure-diagnostics-17315.test.ts, tests/unit/cli/tray-runtime-entry-17315.test.ts, tests/unit/cli/tray-runtime-windows-esm-import.test.ts), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

commit 033f012fc9b25e302debe59e9669b749d3fcd992
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:02:17 2026 -0300

    fix(ci): restore strict core types without runtime changes (#17857)

    Restore strict core types for the stream/cache helpers without runtime changes.

    Evidence: focused tests pass (tests/unit/compute-finish-reason-coverage.test.ts, tests/unit/proxy-dispatcher-cloud-reap.test.ts, tests/unit/semantic-cache-secret-storage-14484.test.ts), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

commit 3fc7d04a36d4e361daf8dc95540fe26dff104f0f
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:02:06 2026 -0300

    fix(ci): reject dependencies that drift from candidate lockfile (#17854)

    Reject CI installs whose dependencies drift from the candidate lockfile.

    Evidence: focused tests pass (tests/unit/build/verify-ci-install.test.mjs), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

commit 7d3a4787130c23e7eb5fa5483d9f468bbd061f5e
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:01:54 2026 -0300

    fix(ci): provision verified workflow scanners for release observers (#17846)

    Provision verified, pinned scanners for the Release-Green observer jobs.

    Evidence: focused tests pass (tests/unit/release-green-scanner-setup.test.mjs), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

commit 0d96ef1748dd765a289186b0fb31e3555117358d
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:01:41 2026 -0300

    fix(sse): surface midstream keepalive read failures (#17292) (#17341)

    Signal upstream read failures after partial output in the early-stream keepalive path.

    Evidence: focused tests pass (tests/unit/early-stream-midstream-failure-17292.test.ts, tests/unit/earlyStreamKeepalive.test.ts), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

    Fixes #17292

commit 4fa23c64d7263f8eb346fad521e20e930e8a3439
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:01:30 2026 -0300

    fix(sse): preserve provider-native model IDs (#16823) (#17336)

    Preserve known provider-native model IDs through model resolution.

    Evidence: focused tests pass (tests/unit/16823-native-model-aliases.test.ts, tests/unit/16823-ollama-tags-persist-dispatch.test.ts), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

commit b69588a1e0585e658602a730f4f2dab7955cd219
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:01:18 2026 -0300

    fix(api): explain the Windows EBUSY/EPERM failure of the in-app npm update (#17335)

    Explain the Windows EBUSY/EPERM failure of the in-app npm update with an actionable message.

    Evidence: focused tests pass (tests/unit/system-update-install-error-windows-ebusy.test.ts), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

commit 3c90c0636832ec3b1c7f7d7871fa17933c721211
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:01:05 2026 -0300

    fix(sse): preserve explicit adaptive thinking effort (#17333)

    Preserve an explicitly requested adaptive thinking effort instead of overriding it.

    Evidence: focused tests pass (tests/unit/16662-explicit-adaptive-effort.test.ts, tests/unit/thinking-budget.test.ts), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

commit 5e146a4a75f4bf8bf7c62575615fcc12979f980d
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Sat Oct 24 23:00:55 2026 -0300

    fix(usage): preserve cache reads after compatibility zeros (#17297) (#17327)

    Preserve cache-read token counts when a compatibility layer reports zeros.

    Evidence: focused tests pass (tests/unit/issue-17297-cache-read-zero-compat.test.ts), validated in one combined board of 9 PRs on origin/release/v3.8.53 with typecheck:core, check:open-sse-typecheck and ESLint clean; file-size OK; changelog integrity OK against origin/release/v3.8.53.

    ⚠️ base-red inherited: #17219

    Fixes #17297.

commit 8f2120868bff087673e402c517e1caa7320dca12
Author: Felipe Marques <contato@felipemarques.com.br>
Date:   Fri Oct 23 01:09:15 2026 -0300

    feat(audio): add ElevenLabs sound effects and music generation (#17287)

commit 6c928202d71d2ce7e0b94802ac6dfe0aa1278b48
Author: Fouad Salkini <fouad@wecansync.com>
Date:   Thu Oct 22 23:21:03 2026 +0300

    feat(api): add OpenRouter-format prices to /v1/models entries (#17259)

commit 3f83d37acefc3a5414d21530586e46d7dd31c814
Merge: 76f4325ab9 daca9902e8
Author: DarknessKiller <mrdarknesskiller@protonmail.com>
Date:   Mon Oct 26 02:02:33 2026 +0800

    Merge commit 'daca9902e859bc1aa95ba276e3ef200ba7f5d142' of https://github.com/diegosouzapw/OmniRoute into release/v3.8.53-mrdarknessk

commit daca9902e859bc1aa95ba276e3ef200ba7f5d142
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Thu Oct 22 14:37:22 2026 -0300

    docs(i18n): re-translate GUARDRAILS mirrors (66 locales) (#17950)

commit 34e3d1f99834f1ca4ca4724eb872ca5e9722c383
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Thu Oct 22 14:34:24 2026 -0300

    refactor(security): quantifier-free edge strip in combo step slugs (CodeQL #1165) (#17906)

    slugify() in src/lib/combos/steps.ts strips the edge separator with /^-|-$/g instead of /^-+|-+$/g. The preceding replace already collapses every separator run into one dash, so the output is identical; tests/unit/combo-step-id-slug.test.ts pins the id shape.

commit da030357292857aed4e2e38d727f92c471e7aba9
Author: leoplife <leonidasl.1010@gmail.com>
Date:   Thu Oct 22 14:30:50 2026 -0300

    feat(config): refresh OpenCode Zen free models (#17345)

commit d63ab780f8aa7d1efd704a365fd8835728924788
Author: Fabio Junior Gruchovski <122614728+fabioKoruja00@users.noreply.github.com>
Date:   Thu Oct 22 14:30:32 2026 -0300

    fix: honor explicit combo selection in MCP and A2A (#17188) (#17235)

commit dc648dfce195861964d2d0d0f5b4731a37cd00b6
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Thu Oct 22 13:53:45 2026 -0300

    fix(ci): accept Dependabot's support@github.com sign-off in check:ai-attribution (#17876)

    Dependabot ends every bump commit with Signed-off-by: dependabot[bot] <support@github.com>; the exemption only matched the noreply co-author address, so Lint and Fast Quality Gates failed on every Dependabot PR. The exemption now accepts that exact name with either exact address; look-alike names with the same address still fail.

commit 0482755c5e4faeeb23736b9e56ec293d84809efa
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Thu Oct 22 08:47:26 2026 -0300

    docs(i18n): ENVIRONMENT — carry the CodeBuddy ∅ (∞) default in source + 66 mirrors (base-red fix for cli-env-quality-docs-mirrors) (#17929)

commit b7a61c8d107ddc9771696e8c12be86736e778629
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Thu Oct 22 08:42:36 2026 -0300

    docs(i18n): re-translate CLAUDE.md mirrors (66 locales) — protected surface, owner approval required (#17910)

commit 00f340bc211603cb6083ceca22fe093fe13bbaac
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Thu Oct 22 08:22:59 2026 -0300

    docs(i18n): re-translate the ENVIRONMENT mirrors after #17074 (#17919)

commit cce4f5bc2579f4a4cb66471901a0fd835d6dd1a3
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 12:53:17 2026 +0200

    fix(batch): preserve CSV source when remapping input (#17222)

commit 4bb1c22b055046a266a2b89ac699e1ca58673d6e
Author: MeTony <70nyIT@users.noreply.github.com>
Date:   Thu Oct 22 12:52:52 2026 +0200

    fix(tools): keep client-declared filePath in the Read shim (#17294) (#17295)

commit b68c33211e99aa24ce3ec04a1a68280b0bd8395d
Author: Fabio Junior Gruchovski <122614728+fabioKoruja00@users.noreply.github.com>
Date:   Thu Oct 22 07:34:49 2026 -0300

    Fix/17043 claude observed quota reset release (#17107)

commit 24f32794f925e25afdfe7800b4206c6dbaceb8fd
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 12:32:30 2026 +0200

    fix(batch): discard file reads after input format changes (#17221)

commit c9d9a2ef06fc15d95498fb4ef7f222597bf2edbb
Author: Rafa Martins <146174365+rafacpti23@users.noreply.github.com>
Date:   Thu Oct 22 07:32:13 2026 -0300

    fix(sse): stop quota-share block from crashing the proxy and cooling the shared connection (#17132) (#17214)

commit 0a4a06eebb4b38d3698ad4578274016d45b412af
Author: Aldo <github@aldo.pw>
Date:   Thu Oct 22 04:32:01 2026 -0600

    fix(translator): contain malformed upstream payloads (#17198)

commit 4f8e62017d58cdfa985f4b956a9684090dfa0c16
Author: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Date:   Thu Oct 22 07:21:42 2026 -0300

    docs(i18n): RESILIENCE_GUIDE — fix the lock-scope code span, carry the #16319 contract block in source + 66 mirrors (base-red fix) (#17921)

commit f5452822a84a6c76ed5059087b475946373e351c
Author: Mayank Jaiswal <mayankjaiswal1707@gmail.com>
Date:   Thu Oct 22 15:27:35 2026 +0530

    test(api): lock env master key bypass in model catalog auth (#17340)

commit f2070a84fde166e90d58ba0a95f619bda0e66099
Author: r2jaff <r2jaff000@gmail.com>
Date:   Thu Oct 22 12:57:25 2026 +0300

    test(sse): pin spec reporter in the Perplexity stream boundary test (#17321)

    The test spawns its fixture with node --test and asserts on spec
    reporter output ("ℹ tests 8"), but did not pass --test-reporter.
    Node 22 defaults to the tap reporter when stdout is not a TTY, so the
    assertion failed on Node 22 (supported by engines: >=22.22.2) while
    passing on Node 24, where spec is the default. Pin the reporter the way
    tests/unit/helpers/runIsolatedBoundaryFixture.ts already does.

    Co-authored-by: Claude <noreply@anthropic.com>

commit 141844081819e333a4b251e0a9b62acc014f2b7c
Author: Calvin Chen <mrdarknesskiller@protonmail.com>
Date:   Thu Oct 22 17:57:15 2026 +0800

    fix(compression): keep Responses tool output and injected messages intact (#17310)

    * fix(compression): treat trailing Responses tool outputs as the current turn in lite

    An OpenAI Responses API tool turn carries no assistant message: the call and its
    output arrive as top-level input items, which the adapter turns into a
    synthesized role:"tool" message. lite derived the current turn from the last
    assistant message and defaulted to protecting nothing when there was none, so
    the newest function_call_output was truncated and the agent kept re-reading the
    same file. Trailing Responses tool outputs now count as the current turn.

    Adds isResponsesToolOutputMessage() to the body adapter for the check.

    * fix(compression): restore engine-injected messages into Responses input

    The Responses adapter maps input[] items to chat messages and maps the result
    back by source index. A message an engine injects has no source item — CCR
    prepends its protocol instruction — so the restore loop skipped it and the
    instruction never reached the provider. The model then saw [CCR retrieve]
    markers with no protocol telling it how to expand them. Injected messages are
    now restored as new input items ahead of the mapped ones.

    * refactor(compression): share the Responses input restore tail to clear the cognitive-complexity ratchet

    Extract finalizeResponsesInput() for the two identical string-input + injected-items branches in restore(), add a test for the string-input path and a changelog fragment.

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 90d22e0c826db242a9485b37f1867caea61cc2b3
Author: Mayank Jaiswal <mayankjaiswal1707@gmail.com>
Date:   Thu Oct 22 15:27:04 2026 +0530

    fix(sse): honor Retry-After hint and jitter intra-URL retry backoff (#17306)

    * fix(sse): honor Retry-After hint and jitter intra-URL retry backoff

    * chore(changelog): add fragment for retry-after hint and jitter intra-URL retry

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 6a9b1e2efd1f58eb5bad4f3b3435f2ea87c9b331
Author: Junior-HJ <sjaradj@ucvvirtual.edu.pe>
Date:   Thu Oct 22 04:56:54 2026 -0500

    fix(cursor): bridge Grep when the declared pattern carries a length bound (#17298)

    * fix(sse): bridge Cursor Grep when the declared pattern declares a length bound

    * chore(sse): prefix the changelog fragment with its PR number

commit 5ad89354d140b5f34bdfb91109d6c24fb949ae69
Author: Fouad Salkini <fouad@wecansync.com>
Date:   Thu Oct 22 12:56:44 2026 +0300

    fix(models): stop the context-window reconcile from blocking the event loop (#17296)

    * fix(models): stop the context-window reconcile from blocking the event loop

    Each auto:discovery override write bumped the model catalog cache version,
    so the next getCatalogWindow call in the same pass rebuilt the synced vision
    verdict map from every synced catalog. On ~6,300 synced models with ~1,400
    divergent windows a pass blocked the event loop for ~10.7 s, and since #11016
    a pass runs after every synced catalog write.

    The pass now writes without invalidating and bumps the catalog version once
    at the end, and it skips rewriting an override that already holds the
    discovered window.

    * chore(changelog): name the reconcile-stall fragment after #17296

    * chore(changelog): link the reconcile-stall fragment to #17296

    * refactor(models): split reconcileContextWindows into small helpers to clear the cognitive-complexity ratchet

    Behavior is unchanged: the per-entry decision moves into reconcileOne with isAlreadyPinned and dropStaleAutoPin, so the new-code cognitive-complexity gate reports 0 violations for contextWindowResolver.ts. Changelog fragment notes the refreshedAt semantics.

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 7e8f8f8bfa68e74064044357e70195c21d1851b0
Author: Nick Carter <mr.nicholas.b.carter@gmail.com>
Date:   Thu Oct 22 05:56:34 2026 -0400

    fix(sse): preserve restored thinking in active tool cycles (#17288)

    * fix(sse): preserve restored thinking in active tool cycles

    * docs(changelog): add fragment for preserving restored thinking in active tool cycles

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit f4cf8441786800478603fdde52b9f0bd74bb3136
Author: Dizzle <112548150+maxmad64bis@users.noreply.github.com>
Date:   Thu Oct 22 11:56:24 2026 +0200

    fix(calls): measure content presence on streamed replies (#17277)

    a streamed reply was always logged as empty because the reader read the envelope as a body; the summary is now measured like any other body

commit 9bbc2e37558328ef1dc7bec16c93651ca6b8a3fd
Author: Stanislav <lazygatto@gmail.com>
Date:   Thu Oct 22 12:56:13 2026 +0300

    fix(usage): release the captured provider request body when its dispatch settles (#17269)

    * fix(usage): release the captured provider request body when its dispatch settles

    The Capture object returned by createPreparedRequestLogger lives in an
    AsyncLocalStorage store, so sockets and streams spawned during the dispatch
    keep the store — and its latest.bodyString — alive after the request is done.
    For agent traffic with base64 screenshots that string is 3-10 MB, which
    remained reachable forever: heap climbed ~4-8 MB/h (day averages
    847 -> 1314 MB over three days) until the pressure guard.

    runWithCapture now calls an optional Capture.release() in its finally, so the
    retained body is dropped as soon as the dispatch settles; body(fallback) already
    falls back to the transformed body once latest is gone.

    Follow-up to #16752. Measured on a self-hosted instance over 25.8 h after the
    patch: heap plateaued at 241-388 MB (0.5 MB/h), RSS 790-1048 MB, zero guard
    trips.

    * chore: rename changelog fragment with the PR number (#17269)

    * fix(usage): reject late captures from settled dispatches

    * fix(usage): keep the captured wire body readable after dispatch, release it from the ALS store instead

    Capture.release() cleared latest() when the dispatch settled, but chatCore reads providerRequestCapture.latest()/body() after that point (signature-failure diagnostics, call-log request body). AsyncLocalStorage now holds a small holder whose capture field is nulled in the finally of runWithCapture: sockets and streams that outlive the dispatch no longer pin the Capture and its multi-megabyte body, while chatCore keeps the Capture through its own closure. The settled-dispatch guard stays. Tests now assert the post-dispatch read still returns the wire body and the holder is empty.

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 6b2926286f69cd1872333510e1569dfc3ff84c1b
Author: aminHZDEV <107117131+aminHZDEV@users.noreply.github.com>
Date:   Thu Oct 22 13:26:03 2026 +0330

    fix(api): list only callable models in a restricted key's /v1/models (#17261)

    * fix(api): list only callable models in a restricted key's /v1/models

    A key restricted to cc/* listed other providers' claude-* rows
    (dva/claude-opus-5-high, ddgw/claude-haiku-4-5) that every chat request
    refused with 403. The catalog filter also checks a row's bare root (#781),
    and with preferClaudeCodeForUnprefixedClaudeModels on, the permission check
    expands a bare claude-* name to cc/<name>, which cc/* matches. Dispatch
    checks only the requested id.

    - The root fallback now also needs a bare allowlist entry matching the
      root, which is the case #781 was added for (allowedModels: ["gpt-4o"]).
    - isModelAllowedForKey judges no-think/<provider>/<model> as the real
      model, as the chat handler strips the prefix before its key check. The
      key's catalog now keeps the no-think/cc/ variants it can call.

    * docs(changelog): add fragment for #17261

    * docs(changelog): credit @aminHZDEV in the #17261 fragment

commit 3414466b4ba187e9bd93c21153c6449b6934dcf9
Author: aminHZDEV <107117131+aminHZDEV@users.noreply.github.com>
Date:   Thu Oct 22 13:25:53 2026 +0330

    fix(pricing): price claude-sonnet-5-5 on the cc provider (#17260)

    * fix(pricing): price claude-sonnet-5-5 on the cc provider

    The cc table priced claude-opus-5-5 but had no claude-sonnet-5-5 entry, so
    getPricingForModel("cc", "claude-sonnet-5-5") returned null. Use the shared
    Sonnet 5 tier ($2 / $10, cache read $0.20), which Anthropic lists for
    Sonnet 5.5.

    * docs(changelog): add fragment for #17260

    * docs(changelog): credit @aminHZDEV in the #17260 fragment

commit 6e12c409f7278004475eee697450f5c57b76bba9
Author: Calvin Chen <mrdarknesskiller@protonmail.com>
Date:   Thu Oct 22 17:55:43 2026 +0800

    fix(compression): compact Anthropic tool_result content in headroom e… (#17258)

    * fix(compression): compact Anthropic tool_result content in headroom engine

    * docs(changelog): add fragment for headroom tool_result compaction

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 62ecd3deaf46b894574ced9b3df4cd38216d5081
Author: Patryk Kopyciński <patryk.kopycinski@elastic.co>
Date:   Thu Oct 22 11:55:32 2026 +0200

    fix(translator): hoist nested tool_result.content cache_control on the openai-compat and openai→claude paths (#17257)

    * fix(translator): hoist tool_result.content cache_control to block level for openai-compat downstream re-translation

    An OpenAI `role:"tool"` message carrying `cache_control` on its content blocks
    reached Anthropic as `tool_result.content[i].cache_control`, which is hard
    rejected:

      [400] messages.N.content.0.tool_result.content.0.cache_control:
      cache_control may not be specified within `tool_result.content`.
      Instead, place it directly on `tool_result`

    filterToOpenAIFormat() early-returned tool messages untouched, so nested markers
    survived into openai-to-claude's tool_result block. The existing
    prepareClaudeRequest hoist (6d45da2050) could not catch this: it only runs for
    targetFormat === "claude", while this path is sourceFormat=openai →
    targetFormat=openai into an openai-compatible gateway that itself re-translates
    to Anthropic.

    Two surgical changes:
    - filterToOpenAIFormat strips nested markers off tool content blocks and, in
      preserve mode, hoists the first one onto the tool MESSAGE.
    - openai-to-claude emits that message-level marker on the `tool_result` block.

    Covered by tests/unit/openai-tool-message-cache-control-400.test.ts (5 cases
    across strip and preserve modes). Mutation-checked: reverting either change
    turns 4/5 red.

    * chore: drop replay scratch file

    * fix(translator): hoist nested tool_result.content cache_control in openai-to-claude itself

    Replaying the captured 157-message live body showed the previous commit only
    covered the openai→openai (openai-compatible passthrough) leg. The DIRECT
    openai→claude leg still emitted 2 nested markers, because the Claude-side
    hoist from 6d45da2050 lives on another branch line and is not an ancestor of
    this branch — and that path never calls filterToOpenAIFormat.

    Add hoistToolResultCacheControl() in openai-to-claude and apply it at BOTH
    tool_result construction sites (the role:"tool" branch and the user-turn
    tool_result part). An explicitly provided block-level marker wins over a
    nested one; a tool_result with no marker gains no cache_control key.

    Replay of the live body is now clean on all four combinations:
      openai->openai  preserve=false/true  0 nested markers
      openai->claude  preserve=false/true  0 nested markers
    Mutation-checked: neutering the helper returns the direct leg to 2 markers.

    Suite grows 5 -> 9 cases. Targeted regression run (414 cache/translator/
    openai/claude/tool files): 3334 pass, 1 fail — kiro-tool-call-validation,
    pre-existing and reproduced with these changes stashed out.

    * refactor(translator): rename to hoistNestedToolResultCacheControl to avoid shadowing claudeHelper's body-level guard

    openai-to-claude.ts and claudeHelper.ts both exported hoistToolResultCacheControl
    with different signatures (content-level vs body-level). No cross-import so it
    compiled, but the name collision is a readability trap. The openai-to-claude one
    operates on a single tool_result's content array, so name it accordingly.

    * docs(changelog): add fragment for #17257

commit 0212597367e2a169cb341f4b3dc6cc03e668c856
Author: Edo Sulai <edosulai@icloud.com>
Date:   Thu Oct 22 16:55:21 2026 +0700

    fix(antigravity): keep Claude tool results before merged user text (#17256)

    * fix(antigravity): keep Claude tool responses before merged user text

    * style(antigravity): prettier-format the Claude tool-result order change and add its changelog fragment

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 4801197b0f063fce8c7bcf5eaf9c67265b32e09e
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:55:10 2026 +0200

    fix(dashboard): ignore obsolete initial provider loads (#17253)

    * fix(dashboard): ignore obsolete initial provider loads

    * docs(changelog): record #17253

commit 90c00f61a6a4045e52e04e5f60aae9d0c6fcc269
Author: Daniil Karpenko <dankarrr@gmail.com>
Date:   Thu Oct 22 13:55:00 2026 +0400

    fix(capabilities): resolve compat vision overrides for prefix-addressed custom nodes (#17252)

    * fix(capabilities): resolve compat vision overrides for prefix-addressed custom nodes

    #15086 made the canonical capability resolver read
    `modelCompatOverrides.supportsVision`, but the lookup is keyed by the provider id
    `PUT /api/provider-models` received — normally a custom node's internal id
    (`openai-compatible-responses-<uuid>`). The catalog addresses the same node by its
    user-defined prefix (`catalog.ts`'s `alias = prefix || providerIdToAlias[providerId]
    || providerId`), and so do prefix-addressed combo steps, so the operator's vision
    flag stayed invisible on that path: an image request to a prefix-addressed target
    was rejected as vision-incapable even though the model does accept images.

    `getProviderNodeIdByPrefixSync()` adds the missing reverse index (prefix -> node id,
    built from the same per-DB memo as the existing id -> prefix lookup), and the compat
    vision lookup consults the mapped node id LAST so a direct key match always wins and
    nothing that resolved before can change. Verified with a regression test that fails
    on the base tip (4 of 5 cases) and passes with the fix; the node-id form, the prefix
    form and the catalog-shaped string form now agree, an explicit `false` still flows
    through, and unrelated providers keep their own verdict.

    * chore(changelog): add fragment for #17252

commit e7f22bae0b952854bdfb9225dd38186ccba42237
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:54:50 2026 +0200

    test(cursor): cover keyed mutex successor cleanup (#17251)

    * test(cursor): cover keyed mutex successor cleanup

    * docs(changelog): record #17251

commit 07b78c97345385bc2c189dc1c401fdf4286b9444
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:34:50 2026 +0200

    test(batch): cover JSONL sampling boundaries (#17249)

    * test(batch): cover JSONL sampling boundaries

    * docs(changelog): record #17249

commit e3aa3151fb028618fe4c885a887b48216f79820d
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:34:39 2026 +0200

    test(batch): exercise forbidden CSV mapping paths (#17248)

    * test(batch): exercise forbidden CSV mapping paths

    * docs(changelog): record #17248

commit 10b0f292ad3f9c833f520e3ebc7592f436fe156b
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:34:29 2026 +0200

    fix(sse): preserve refusal deltas in buffered OpenAI SSE (#17246)

    * fix(sse): preserve refusal deltas in buffered OpenAI SSE

    * docs(changelog): record #17246

commit 9f32603b28613500e36d26c86d6d80af83fdf32d
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:34:19 2026 +0200

    fix(sse): ignore non-object events in buffered OpenAI SSE (#17245)

    * fix(sse): ignore non-object events in buffered OpenAI SSE

    * docs(changelog): record #17245

commit 8586d1d4786a9c413f262883da8fc1a4a03df270
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:34:07 2026 +0200

    fix(batch): require content in CSV mappings (#17244)

    * fix(batch): require content in CSV mappings

    * docs(changelog): record #17244

commit 639cf25fa0de37120a8a20f0f169ccca6fc6deef
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:33:57 2026 +0200

    fix(proxy): preserve malformed percent escapes in subscription labels (#17242)

    * fix(proxy): preserve malformed percent escapes in subscription labels

    * docs(changelog): record #17242

commit f233d031e01ea3211a532c8202ac63ea0d43b080
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:33:45 2026 +0200

    fix(proxy): preserve default ports in subscription URIs (#17241)

    * fix(proxy): preserve default ports in subscription URIs

    * docs(changelog): record #17241

commit d77ebe1010e15a9ef58a6e509820e9069b3e148e
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:33:34 2026 +0200

    fix(batch): preserve quoted multiline CSV headers (#17240)

    * fix(batch): preserve quoted multiline CSV headers

    * docs(changelog): record #17240

commit 847931fba1002a8fbd63619a5053a83083b94f49
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:33:23 2026 +0200

    fix(sse): parse native Gemini events in buffered responses (#17239)

    * fix(sse): parse native Gemini events in buffered responses

    * docs(changelog): record #17239

commit 9085a2a4079abff697be677573d092dc1c43da62
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:33:13 2026 +0200

    fix(dashboard): preserve URL continuation in copied cURL commands (#17238)

    * fix(dashboard): preserve URL continuation in copied cURL commands

    * docs(changelog): record #17238

commit dab9ce1d28f1ddf9c535692f7fb10808932f3d94
Author: Edo Sulai <edosulai@icloud.com>
Date:   Thu Oct 22 16:33:03 2026 +0700

    fix(api): honor native model aliases in audio transcriptions (#17232)

    * fix(api): honor native model aliases in audio transcriptions

    * refactor(api): keep audio alias dispatch within complexity budget

    * test(audio): prove native alias through HTTP multipart

    * fix(ci): dispose compression timer and scope mirror provenance checks

    * chore(changelog): name the audio alias fragment after PR #17232 and credit the author

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 90843005a09e738d2c5ac3a402b86bf8c009217f
Author: Fabio Junior Gruchovski <122614728+fabioKoruja00@users.noreply.github.com>
Date:   Thu Oct 22 06:32:52 2026 -0300

    docs(mcp): correct tool-cardinality activation note (#17185) (#17231)

    * docs(mcp): correct tool-cardinality activation note (#17185)

    * test(mcp): cover default manifest and combined tool-filter precedence (#17185)

    * docs(mcp): remove stale activation claim from cardinality tests (#17185)

    * docs(changelog): register PR #17231 release fragment

commit 040561f4b128530ce03f0ec0f20c8a8f180a1ec0
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:32:39 2026 +0200

    test(sse): drain call logs before thinking test cleanup (#17224)

commit 2e18ee71d0e001610122467282fa4d6751bccfd3
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:32:29 2026 +0200

    test(cache): wait for loaded provider data before asserting stats (#17220)

    * test(cache): wait for provider data before asserting stats

    * docs(changelog): reference cache wait PR 17220

    * docs(changelog): file the cache page wait fragment under maintenance

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 7695ba8f3c487bf952e010ffd9970b6f097e7c10
Author: Praveen K Palaniswamy <praveen.palaniswamy@gmail.com>
Date:   Thu Oct 22 05:32:18 2026 -0400

    fix(cli): make restart return by starting the new server detached (#17168) (#17218)

    * fix(cli): make restart return by starting the new server detached (#17168)

    omniroute restart stopped the daemon and then called runServe() without
    daemon: true, so it took the foreground supervisor path and never returned
    to the shell, which made it unusable from scripts and scheduled tasks.

    Pass daemon: true so the restarted server detaches like serve --daemon.
    runRestartCommand() now accepts injectable stop/sleep/serve deps (same
    pattern as runStopCommand) so the regression test never touches a real
    server. Update the stale update.mjs comment that described restart as
    foreground.

    * chore(changelog): add #17218 fragment

commit 3fef1092a9684e09b123cc01152871a0e0bf985f
Author: Rafa Martins <146174365+rafacpti23@users.noreply.github.com>
Date:   Thu Oct 22 06:32:07 2026 -0300

    fix(executors): normalize Bedrock tool_use ids to ^[a-zA-Z0-9_-]+$ with pairing preserved (#17143) (#17217)

    * fix(executors): normalize Bedrock tool_use ids to ^[a-zA-Z0-9_-]+$ with pairing preserved (#17143)

    Bedrock validates tool-use ids against ^[a-zA-Z0-9_-]+$ and 400s the whole
    request when replaying history carrying anything else. The Bedrock executor
    only trimmed ids, forwarding VS Code / IDE-bridge ids with dots, colons and
    slashes verbatim, so any conversation with such tool history failed with
    'messages.N.content.0.tool_use.id: String should match pattern'.

    normalizeToolUseId now replaces out-of-pattern characters deterministically
    with a digest of the original: tool_use and tool_result go through the same
    helper so the pair normalizes to the same string, distinct originals stay
    distinct, and already-valid ids pass through exactly unchanged.

    * fix(executors): preserve Bedrock tool IDs across normalization collisions

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 80dae14fec596bdaa8c5aa01bb288e921c10f715
Author: Rafa Martins <146174365+rafacpti23@users.noreply.github.com>
Date:   Thu Oct 22 06:31:57 2026 -0300

    chore(db): remove unreachable default-settings seeding from runMigrations (#17186) (#17216)

    The post-migration guard checked appliedRecords.some(name startsWith
    '051_'), but record names are the filename regex's SECOND capture group
    (text after the numeric prefix), so the branch could never execute and
    orphaned insertDefaultDatabaseSettings() plus its DEFAULT_DATABASE_SETTINGS
    import. The defaults it would have seeded are already supplied by
    merge-on-read (optimizationSettings.ts), so removal keeps behavior
    exactly; a tombstone comment records why the guard was dead.

    Pinned by an invariant test over the real migrations directory.

commit 6abb163824a3bb35d79d2e744a010aa75840e48e
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:31:46 2026 +0200

    fix(batch): upload complete input files instead of preview samples (#17213)

    * fix(batch): preserve complete input files during upload

    * test(batch): allow time for large CSV upload regression

    * docs(changelog): reference batch upload PR 17213

commit 2a2d98d25f12df2f6e07212663d52a2ad6eb70d1
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 11:31:36 2026 +0200

    fix(batch): reject array-valued Anthropic params in JSONL validation (#17211)

    * fix(batch): reject array-valued Anthropic params in JSONL validation

    * docs(changelog): reference batch params fix PR

commit e8ba277f9d251fb2281658ead86662862c016d2c
Author: seathatflowsinourveins <234074349+seathatflowsinourveins@users.noreply.github.com>
Date:   Thu Oct 22 05:31:26 2026 -0400

    fix(executors): preserve explicit Codex reasoning context (#17210)

    * fix(executors): preserve explicit Codex reasoning context

    Preserve explicit caller reasoning context at the Codex HTTP/WebSocket wire allowlist.
    Keep unsupported reasoning siblings filtered and document the model/mode boundary.

    * chore(changelog): number the fixes fragment for #17210

commit df53b3d1156c375776b6859df06e924115c0b2ce
Author: Rafa Martins <146174365+rafacpti23@users.noreply.github.com>
Date:   Thu Oct 22 06:31:16 2026 -0300

    fix(sse): stop injecting outputDimensionality into Gemini embeddings (#17158) (#17208)

    * fix(sse): stop injecting outputDimensionality into Gemini embeddings (#17158)

    Google's OpenAI-compatible embeddings endpoint honors the `dimensions`
    field and rejects the camelCase Gemini-native `outputDimensionality`
    with 400 "Unknown name", so every sized Gemini embedding request failed
    and only the default 3072-dim output stayed reachable. The native
    embedContent transport already maps `dimensions` to snake_case
    `output_dimensionality` itself, so the translation is unnecessary on
    both paths. Forward `dimensions` untouched and refresh the stale combo
    schema comment.

    * docs(changelog): add fragment for the Gemini embeddings outputDimensionality fix

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit bdcd6c0088f7a68bb0d18b258fcdc1d6cd44c9ce
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:51:26 2026 +0200

    test(providers): cover the Microsoft Designer web retirement helpers (#17207)

    Pin the retired provider id matching, the 410 error raised for a retired provider and the guard that recognises exactly that error.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 9106ec32df86651cf76acb6e5a3a95ca43046aa6
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:51:16 2026 +0200

    test(dashboard): cover the hidden sidebar group normalisation (#17206)

    Pin the normalisation of the hidden sidebar groups setting: non-arrays give none, only known ids are kept, duplicates collapse and the canonical order wins.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 3dbced5dc1d68016bb906c956a0f7ea26248b880
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:51:06 2026 +0200

    test(cli): cover buildHealthcheckPath (#17205)

    Pin that the Node bin directory is appended to the healthcheck PATH unless it is already an exact entry, using path.delimiter so the cases hold on Windows.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 57f487b4780694c634c3f94d7fc1b34585a242e9
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:50:56 2026 +0200

    test(providers): cover the custom model metadata precedence (#17204)

    Pin that a custom model overlays only the fields it stores (null counts, undefined does not), and how discovered and custom model lists merge by id.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 6f626bde79b91b2e4b7ffc9ec305d227df7c5dd3
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:50:46 2026 +0200

    test(analytics): cover sortProviderDailyUsageRows (#17203)

    Pin the request-count table sort: string columns by localeCompare, numeric columns as numbers, ascending and descending, and the input array left in order.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 0f76b4e3a1376d66eaa411df1a0607940ded9de8
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:50:36 2026 +0200

    test(proxy): cover normalizeProxyHostForLog (#17202)

    Pin the proxy log host normalisation: trim, one pair of IPv6 brackets removed, lowercase, and null for anything that is not a non-empty string.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit d4b9ca8547b5c20a3a0347fd9bcdda48ce4623f6
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:50:25 2026 +0200

    test(rate-limit): cover the connection override mapping (#17201)

    Pin how a per-connection override maps onto Bottleneck options (concurrency, minimum time, a per-minute reservoir), that zero or invalid values fall through, and how the override map is loaded.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 823c9c33dfa78dcebe1e8f97ba96f5bf1204c509
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:50:15 2026 +0200

    test(trae): cover parseTraeApiHost and resolveTraeApiHost (#17200)

    Pin that only a plain https origin under trae.ai is accepted for the token refresh host, that look-alike hosts, credentials, ports and other protocols are rejected, and that the default region is the fallback.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 80482f49763d08694814422da4d8cdc93aa7bfff
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:50:05 2026 +0200

    test(qwen): cover the thinking tool_choice sanitiser (#17199)

    Pin when Qwen thinking is active, which tool_choice values are incompatible with it, and that only that combination is rewritten to auto with a warning that names the provider.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 0f1505f7fdad297c6e2905311baabee31a3da5bb
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:49:53 2026 +0200

    test(fallback): cover isRequestScoped400 (#17194)

    Pin which 400 responses are request-scoped and so do not rotate accounts: parameter, context overflow and malformed request texts, while credential and throttling texts and other statuses are not.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 68e1572858053bff2c4ff8cca3d6329e042e6abb
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:49:43 2026 +0200

    test(sse): cover normalizeFinalOpenAIStreamChunk (#17193)

    Pin the final stream chunk normalisation: numeric chunk and tool-call ids become strings, aliased tool names are restored, and the result reports changes and the finish reason of the first choice.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 0568c16789c323b0fb39f468afb77c48d8378314
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:49:33 2026 +0200

    test(proxy): cover sanitizeTransportError (#17192)

    Pin the allowlist of the rethrown transport error: the caller's message, an allowlisted code or the fallback, the cause code, a short errorCode and a finite statusCode, and nothing else.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit f5c4fa29c13b125342bc2198d449e5a8c37f4de9
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:49:23 2026 +0200

    test(fallback): cover the shared-wallet 402 helpers (#17191)

    Pin which providers and bodies count as a shared-wallet credit exhaustion, the usage balance signal, and the generic credit check that also accepts it. The behaviour is otherwise covered only through the auth integration test.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 4531993a1362b8a5b1d2da62c91c9d7205f9b070
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:49:13 2026 +0200

    test(claude-code): cover the thinking display summary rule (#17190)

    Pin when a compatible request gets display summarized: from the connection setting or the Copilot marker, never for disabled thinking, and how an explicit display interacts with each.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit b42e271cdddc28cac91ffad7c7db436835650e86
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:49:03 2026 +0200

    test(gemini): cover the pasted cookie normalisation (#17189)

    Pin how a bare value, a cookie header and a flat or wrapped JSON export are normalised, and when the validation variant strips the Cookie prefix or returns null for a missing PSID.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit b93849a12d1b7cc8c63daf911044436d613e7c89
Author: 小妍儿 ✨ <258399167+xiaoyaner0201@users.noreply.github.com>
Date:   Thu Oct 22 15:48:53 2026 +0800

    fix(cursor): preserve structured model entitlement errors (#17182)

    * fix(cursor): project typed Connect errors and scope weekly model denials

    * fix(cursor): preserve buffered stream errors and JSON whitespace

    * fix(cursor): classify pre-output stream failures at readiness boundary

    * refactor(cursor): extract JSON whitespace predicate

    * fix(cursor): preserve known-type legacy debug error details

    * docs(changelog): record Cursor entitlement error fix for #17182

    * fix(cursor): keep generic ActionRequired quota cues alongside typed error details

    Typed or debug ErrorDetails replaced the envelope message, dropping generic
    ActionRequiredError quota cues and changing otherwise equivalent refusals
    from 429 to 502.

    Keep public details first and unchanged. Append the complete sanitized
    message only within the remaining title+detail budget; omit placeholders,
    duplicates and over-budget messages rather than truncating either text.
    Weekly model entitlement still takes precedence as 404. Public text keeps
    the existing allowlist, credential redaction and original length limits.

    Verified all three focused test files in default and private TMPDIR runs
    (126/126 each), including full-budget, one-character-over and exact-fit
    regressions for typed/debug details. All ten external probe files passed,
    including the independent legacy safety probe (8/8). The full Cursor run
    passed 769 tests with 13 skips after an unchanged retry of one subprocess
    SIGKILL timing assertion. Complexity/dead-code ratchets, core/open-sse/API
    typechecks, lint, mutation coverage and changed-file formatting passed.

commit 0eaf0b3d320161e38c140a0c1f885fb7e896c727
Author: Samar Nathani <samarnathani0@gmail.com>
Date:   Thu Oct 22 13:18:42 2026 +0530

    fix(cli): gate cmd.exe arg escaping on the resolved spawn shell (#17178)

    On Windows the launch call sites (launch, launch-codex, fcc-claude and run)
    cmd.exe-escaped pass-through arguments unconditionally. That escaping is only
    correct on the `shell: true` path (npm .cmd shims, #8837); the native-installer
    path spawns a real claude.exe/codex.exe with `shell: undefined` (#9454), where
    the escaped text reached the binary literally (e.g. `^^^"-p^^^"`) and every
    launch was silently mangled with exit code 0.

    Add one shared quoteShellArgsForSpawn(args, platform, useShell) helper and gate
    every spawn/plan site on the resolved spec's `shell`, so the `run` dry-run plan
    keeps matching what actually spawns.

    Fixes #17177

commit e6d2cbf76e81d4d6a4ca3408bbdcd4834dfc99b9
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:48:33 2026 +0200

    test(db): cover the API key usage-limit field helpers (#17176)

    Pin the usage-limit flag (true, 1 and the string 1 only), the positive finite USD limits, the snake_case then camelCase read of a row, and the SQL assignments an update appends.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 5bb022b5e46146a79b2036277cae2c29de70d0e6
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:48:23 2026 +0200

    test(translator): cover conversationSignatureFields (#17175)

    Pin which fields enter a tool conversation signature: the tool-call fields that are present, the item type unless it is message, and never role or content.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit a3614d34aca43e03bc61ca2c9cb0a7ccd61a606e
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:48:13 2026 +0200

    test(cli): cover the container config write refusal message (#17174)

    Pin the refusal prefix that callers use to recognise it, the tool label and target path, the host command with its placeholder, and the override hint of the refusing surface.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit c3df8a0719f75d8f855f7ca2db0e5f4785d47ff7
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:48:03 2026 +0200

    test(oauth): cover resolveDashboardPort and buildSshLocalForward (#17173)

    Pin the dashboard port resolution (explicit port, https 443, http or missing 80) and the ssh local-forward command: one -L flag per port, duplicates collapsed, host filled in and the user placeholder left.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit ef008b34ee95230d8db848511c5306e7468fb63c
Author: Paco Cartones <253313177+pacocartones@users.noreply.github.com>
Date:   Thu Oct 22 09:47:53 2026 +0200

    test(combo): cover isRetryAfterEligibleStatus (#17172)

    Pin the statuses allowed to carry a retry-after suffix on an all-targets-failed combo response: 429 and 503 only, never a config-class status such as 422, and nothing that is not a real number.

    Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>

commit 686769758c62154d17d2f2d7402d3795fc886930
Author: Mike Ng <iam@mikeng.io>
Date:   Thu Oct 22 15:21:47 2026 +0800

    fix(cache): version and canonicalize response control identities (#17161)

    * fix(cache): version and canonicalize response control identities

    * docs(cache): add response identity changelog fragment

    * fix(cache): reuse the semanticCache import for canonical JSON and pin the schema:2 plain-text keys

    Re-export canonicalJson from src/lib/semanticCache so the open-sse cache manager adds no new open-sse to src boundary edge (ratchet stays at 774), and align the #15816 plain-text key test to the intentional schema:2 identity while asserting the legacy payload is no longer reused.

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit fa885d4699deb386484e91eff203935cdbeab078
Author: opensource-elearning <159253500+opensource-elearning@users.noreply.github.com>
Date:   Thu Oct 22 12:51:37 2026 +0530

    perf(resilience): memoize resolveResilienceSettings by settings object identity (#17153)

    * perf(resilience): memoize resolveResilienceSettings by settings object identity

    resolveResilienceSettings() is called on every request from chatCore.ts:417
    and performs 13 normalizations + a full legacy fallback rebuild per call.

    The settings object comes from getCachedSettings() which uses a TTLCache
    with a 5s TTL that returns the SAME object reference on cache hits.
    This change adds a WeakMap cache keyed by that exact object reference,
    so repeated requests within the 5s window reuse the fully resolved
    ResilienceSettings object.

    Cold: 13 normalizations + fallback + object construction
    Warm (within 5s): single WeakMap get + return (zero normalizations)

    No behavior change: settings are immutable per request, and the cache
    auto-invalidates when settings are updated (TTLCache evicts old object,
    new object created, WeakMap key differs).

    Tests: resilience-stream-recovery-feature-flags, providerCooldownTracker,
    stream-throughput-watchdog, auto-routing-settings-resolve, cli-resilience-commands
    all pass.

    * test(resilience): cover settings memoization identity and invalidation

    * docs(resilience): note the memoized settings are shared and env defaults frozen per object; add changelog fragment

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit cd8706b36f5f22240a58806585433282d30b0b1c
Author: Dizzle <112548150+maxmad64bis@users.noreply.github.com>
Date:   Thu Oct 22 09:21:27 2026 +0200

    fix(syntx): cap SYNTX media download and data-URL decode at 50 MiB (#17152)

    SYNTX media fetched by URL and the three data-URL decodes loaded unbounded content into memory; the download now enforces a 50 MiB limit (413) and a client abort (499), and the data-URL decodes reject oversized payloads before allocating (400)

commit dbd6492ba9cec6b33bc9788f449cceab21e2dee7
Author: opensource-elearning <159253500+opensource-elearning@users.noreply.github.com>
Date:   Thu Oct 22 12:51:17 2026 +0530

    perf(logging): build the HH:MM:SS clock prefix without a locale formatter (#17149)

    * perf(logging): build the HH:MM:SS clock prefix without a locale formatter

    getTimeString() in streamHandler.ts and usageTracking.ts called
    toLocaleTimeString('en-US', ...) for every stream completion and every
    usage line. Intl formatting dominates that helper; the value it returns
    is a fixed zero-padded HH:MM:SS, so it can be built directly.

    Measured on Node 24.21.0, 100k iterations x 11 alternating rounds,
    production shape (a fresh new Date() inside each call):

      toLocaleTimeString  median 63.19 us/call
      padStart            median  0.30 us/call
      saved                      62.89 us/call

    Two calls per request (one usage line, one stream completion log) is
    ~0.13 ms/request; ~126 s per million requests.

    Output is unchanged: compared the two implementations across 518,400
    second-resolution instants spanning 6 dates (incl. a leap day and both
    DST boundaries) with zero value mismatches, and every value still
    matches the strict CLOCK_TOKEN_RE /^\d{2}:\d{2}:\d{2}$/ the console
    interceptor uses.

    Also drops an unused catch binding in usageTracking.ts that the lint
    gate reported, and prunes its now-orphaned suppression entry.

    New test pins the log prefix shape and fails on a 12-hour-clock
    regression.

    * test(logging): cover clock boundaries and interceptor parsing

    * chore(changelog): add the clock-prefix fragment with credit

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 679c736c59960ad4a90ce9cd3fe19566cabdd34b
Author: opensource-elearning <159253500+opensource-elearning@users.noreply.github.com>
Date:   Thu Oct 22 12:51:05 2026 +0530

    perf(build): enable incremental TypeScript for the open-sse workspace typecheck (#17147)

    * perf(build): enable incremental TypeScript for the open-sse workspace typecheck

    open-sse/tsconfig.json was the one typecheck config left without
    incremental state after #16342 covered the four root configs. The
    open-sse gate therefore re-checked the whole workspace (~26k files,
    checkJs on JS too) on every invocation.

    Adds incremental + tsBuildInfoFile, reusing the existing
    .gitignore:306 '.tsbuildinfo-*' rule, so no build artefacts are
    tracked.

    Measured with node scripts/check/check-open-sse-typecheck.mjs on
    Node 24.21.0 / tsc 6.0.3, 5 runs each:

      cold (no build-info)  25.6s
      warm (build-info)      6.3s

    Correctness: a warm run still emits every diagnostic. Verified by
    injecting a deliberate TS2322 into the workspace, confirming it is
    reported on both the first and second run, then fixing it and
    confirming a clean exit. The gate reports
    openSseTypecheckErrors=0 against the frozen baseline in both states.

    * chore(changelog): name the open-sse incremental typecheck fragment after its PR with credit

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 2b99af7bd9e98b0f37513c90fb479f865b02310f
Author: Devulapalli Naga Sri Vaishnavi <vaishnaviharish2006@gmail.com>
Date:   Thu Oct 22 12:50:53 2026 +0530

    Preserve tool calls in textual reasoning chunks (#17144)

    * fix(responses): preserve tool calls in textual reasoning chunks

    * chore(changelog): name the fragment with the PR number and add the contributor credit (#17144)

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 36613be7064a505c55a3a7ee356776c706488b5d
Author: Saeb Amini <SaebAmini@users.noreply.github.com>
Date:   Thu Oct 22 17:20:40 2026 +1000

    Add Max Output Tokens override to Custom Models section (#17140)

    * Add max-output-tokens override editor to custom models list

    * Add maxOutputTokenOverride field to CompatModelRow type

    * Add maxOutputTokenOverrideInvalid translation key

    * feat(dashboard): translate the max-output-tokens override error in all locales, add UI test and changelog fragment (#17140)

    Adds providers.maxOutputTokenOverrideInvalid to the 66 locale catalogs, a vitest UI test covering load / number / blank-clears / invalid-no-PUT for the Custom Models editor, and the features fragment.

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 5b77da523f0c6f34958cb38a71375b6400d1f137
Author: Dizzle <112548150+maxmad64bis@users.noreply.github.com>
Date:   Thu Oct 22 09:20:29 2026 +0200

    fix(sse): reject shut-down models regardless of id letter case (#17135)

    The lifecycle lookup key ignores letter case on the model side like the snapshot did, so non-lowercase spellings of a shut-down model id get the record verdict (410, which then cites the canonical record id) instead of being routed.

commit 6e4b353549145d6bfd8d677398df8d5e70af9aa6
Author: Dizzle <112548150+maxmad64bis@users.noreply.github.com>
Date:   Thu Oct 22 09:20:16 2026 +0200

    fix(sse): add the compat-rejected last-resort fallback to the universal combo dispatch (#17120)

    the round-robin-only last-resort tier was missing from the shared dispatch loop, so a priority combo whose kept targets were all skipped answered 503 without reconsidering a compat-rejected-but-healthy target; the rejected set is now computed at resolution, threaded into the loop, and probed only when no kept target was ever attempted — each new case fails without the change.

commit 20683172beed44ccf29e86863c3217e5c6eb0808
Author: Andres Felipe Diago Matta <82129635+AndresDiagoM@users.noreply.github.com>
Date:   Thu Oct 22 02:20:04 2026 -0500

    fix(sse): hand Anthropic-format clients their declared tool-name casing from every provider (#17118) (#17119)

    * fix(sse): hand Anthropic-format clients their declared tool-name casing from every provider

    Claude-format responses assembled by openai-to-claude, gemini-to-claude and the
    non-streaming responseTranslator all ended in restoreClaudeToolName(), whose
    canonical upgrade (read -> Read, #7926/#9008/#10392/#11085) assumes the client
    is Claude Code. OpenCode's @ai-sdk/anthropic provider declares lowercase tools,
    so a combo falling back from native claude to Antigravity, Codex or Gemini (or
    any of those used directly over /v1/messages) answered with tool_use.name
    "Read" for a tool declared as "read", and every tool call failed client-side
    ("No tool named Read").

    Lift the #12721 passthrough rule into a shared module and apply it on the
    translate paths too: alias ledger first, then the CLIENT request's declared
    spelling (exact, then case-insensitive), then the legacy canonical upgrade only
    for names the request never declared. Claude Code declares "Read", so it keeps
    getting "Read" back when an OpenAI-style upstream downcases it; OpenCode gets
    "read" back from the same upstream. The client's tools[] is threaded into the
    translate-mode stream state and into translateNonStreamingResponse.

    * docs(changelog): add fragment for #17119

    * fix(sse): keep streamBufferBytes the last parameter of the transform helper

    clientTools now sits before streamBufferBytes, so the trailing buffer-size slot GLM fills (#12925) stays last and GLM's call matches the helper arity. GLM passes null for clientTools; the translate call in streamingTail drops its placeholder undefined.

    * fix(sse): carry declared tools through Claude passthrough (#17118)

    * refactor(sse): split declaredToolCasing helpers under the complexity gate

    findDeclaredToolName (cyclomatic 17, cognitive 18) and restoreDeclaredToolName
    (cognitive 17) were over the new-code limit of 15. Extract declaredNameOf()
    for the per-entry tools[] spelling and aliasLedgerName() for the _toolNameMap
    precedence; behaviour is unchanged and the 17118/17119 suites still pass.

    ---------

    Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

commit 4aa9958bc2966f9d38d325e92f9d709d05a1e51b
Author: Dizzle <112548150+maxmad64bis@users.noreply.github.com>
Date:   Thu Oct 22 09:19:53 2026 +0200

    fix(sse): a second 429 with a shorter cooldown no longer shortens the active cooldown (#17116)

    markRateLimited overwrote the expiry unconditionally, so a second 429 with a shorter cooldown reopened the combo target early; the assignment now keeps the later expiry

commit 2d43b5c83b41970490bdb405615726c9eb41e045
Author: Davy Massoneto <davy.massoneto@yahoo.com>
Date:   Thu Oct 22 04:19:41 2026 -0300

    fix: preserve capped Claude output when fitting thinking budget (#17110)

    * fix: preserve capped Claude output when fitting thinking budget

    * docs: add changelog for Claude thinking budget fix

commit ce826c8c21edd72872c40a80e2d625b1d9a9b884
Author: Junior-HJ <sjaradj@ucvvirtual.edu.pe>
Date:   Thu Oct 22 02:19:12 2026 -0500

    fix(codebuddy): harden gateway compliance and tool schema contracts (#17080)

    * fix(codebuddy): harden gateway compliance and tool schema contracts

    * style(test): format codebuddy gateway hardening test with prettier

commit 5d315a9562064a6eec61cb2fd7832f65db01e991
Author: opensource-elearning <159253500+opensource-elearning@users.noreply.github.com>
Date:   Thu Oct 22 12:49:02 2026 +0530

    feat(sse): let the quota-refusal egress scope cover every provider (#17074)

    * feat(sse): let the quota-refusal egress scope cover every provider

    A 429 received through a proxy pool member set that member aside only for the
    IP-bucketed family (opencode-go, opencode-cli, plus opencode-zen when its flag
    is on). Every other provider kept egressing through the member that had just
    answered 429, so a rate limit could not rotate the address — worst for the free
    and keyless providers, whose quota is per IP because there is no key to bucket
    by.

    rotatesEgressOnQuotaRefusal() widens that write to every provider when the
    operator sets OMNIROUTE_EGRESS_QUOTA_ROTATION_PROVIDERS=all, and keeps the
    IP-bucketed family as the only default. The clear-on-success arm, the
    sibling-connection lockout and every existing default are unch…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants