Nginx redirected GET / (and a token-less index page) to a URL carrying
CCR_WEB_AUTH_TOKEN, so anyone who could reach the published port got the
token from the Location header. The root now redirects to the page and
only passes on a ccr_web_token the caller already supplied. A generated
token is printed once to the container log, as Jupyter does.
Preserve Windows Pi dispatch arguments, serialize raw trace adoption, recover
unexpected core exits without overriding manual stop/start, and avoid unused
token estimation and repeated route script body copies. Preserve custom token
counts and select the configured protocol for bare runtime provider IDs.
Remove all gateway dependency patches and build directly from upstream commit
0a2c0abd48d2144ed0ac2ce4d954a661c51ffcb1. Include reproducing regressions and
update desktop/Docker release packaging and development records.
Validation: CCR targeted regressions 270 passed, 2 Windows skips; upstream
full suite 931 passed, 7 skips; bundled HTTP/SSE/SQLite/process tests 4 passed.
Type checks and both production builds passed.
The Codex backend routes prompt caching by the session_id request header;
prompt_cache_key in the body alone does not hit the cache. Codex upstreams
were skipped entirely, so Claude Code sessions routed to Codex got almost
no cache reads. Add the session_id header (unless already present) and
prompt_cache_key (unless caller-supplied) for Codex, still without metadata.
Fall back to the session id embedded in metadata.user_id and drop values
unsafe for HTTP headers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199MDCTCCgDMRPaiKkrXEGA
raw-trace updates rewrote request_logs.response_headers with the upstream
header set (or "{}" when the set was empty), wiping the gateway-inserted
x-ccr-fallback-attempts header that powers the R{n} retry badge on the log
page. Since updateFromRawTrace only refines rows the gateway already
inserted, keep the incoming headers for usage/billing extraction below but
stop persisting them to the response_headers column.
Adds a regression test asserting x-ccr-fallback-attempts survives raw-trace
updates carrying both a non-empty header set and an empty {} set.
Fixes#1864
A bare model id registered under multiple providers with disjoint
protocol capabilities resolved to undefined, so hop 1 failed locally
and the request silently fell through to the global fallback chain on
a different model. ModelRegistry.resolve() now accepts the request
protocol and uses it to break ties between bare-id matches;
genuinely ambiguous ids (no protocol given, same protocol declared
on both providers, or providers without capability metadata) keep
the previous undefined behavior instead of guessing.
Fixes#1860.
Readable.fromWeb(upstream.body).pipe(transform) leaves the source with no
'error' listener, so an upstream ECONNRESET mid-stream ("TypeError:
terminated") became an unhandled 'error' event and killed the core gateway
process. Use stream.pipeline() in the live token rate transform and the
Codex apply-patch / multi-agent bridges so the error destroys the whole
chain and reaches the response consumer.
The main-process proxy already listens for errors on every stage, but
.pipe() never ended the client response, so the client hung until its own
timeout. Destroy the response once the stream error is logged.
Fixes#1846
Base URL normalization stripped any trailing /models, so a custom
provider URL like https://host/platform/models became
https://host/platform. Only treat /models as the model-list endpoint
when it sits at the root or directly under an API version segment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds Serply to the hosted web search providers and the built-in Fusion
web_search MCP tool, alongside Brave, Bing, Google CSE, Serper, SerpAPI,
Tavily and Exa. Configured with SERPLY_API_KEY and an optional
SERPLY_SEARCH_ENDPOINT override, like the other providers.
The activity grids size their cells from the frame's measured height, but
reserve a hard-coded height for the month label row (10px in the dashboard
widget, 8px in the tray widget). The row actually renders ~20px, and its
`mb-1` margin was never accounted for, so the reservation fell short and
`overflow-hidden` on the frame clipped the bottom row.
Measured on v3.1.1, dashboard widget (dimensions 2x2):
frame (overflow-hidden) 121.5px available
month label row 20px + 4px margin
cells grid 7 x 12px + 6 x 3px gap = 102px
content total 126px -> 4.5px clipped
Reserve the label row's measured height plus its bottom margin instead, and
drop the vertical use of `monthLabelGap` (it is a column gap). The same
dimensions then compute cellSize 11 instead of 12, giving 119px of content
in the 121.5px frame - no clipping.
Verified in the running dashboard after a rebuild: content 119px vs frame
122px (3px slack), bottom row fully visible.
Fixes#1800
kimi.com/code serves mainland China (WeChat / +86 sign-in, Alipay and WeChat Pay), so international readers cannot sign up or pay there.
Add the kimi.ai/code link (Google sign-in, card payment) to the sponsor banner link row and label both plan sites in the intro paragraph of the English and Chinese READMEs, keeping the same aff value.