- Group Antigravity Gemini text models into single 'Gemini (Flash / Pro)' quota
- Group Claude models into single 'Claude (Sonnet / Opus)' quota
- Prune stale or legacy model keys from hidden quota visibility list
Co-Authored-By: Claude Code <noreply@anthropic.com>
## Features
- **Fetch**: add Ollama Cloud web fetch provider
- **Gemini / Antigravity**: add Gemini 3.8 Flash support and bump IDE fingerprint to 2.11.0
- **Claude**: add Claude Fable 5.1 support (adaptive thinking with `output_config.effort`), bump Claude Code fingerprint to 2.1.258 for new-model access
- **Providers**: add client-side status filter (All / Active / Inactive / No connection) on the Providers dashboard; add max height and scroll for connection list
- **Providers & Models**: streamline tokenrouter model catalog down to 22 flagship/newest models and add missing provider icons; refresh Codebuddy-CN catalog (add hy4-preview/hy3/glm-5.3/kimi-k3-1, drop EOL glm-5.0/glm-4.7)
- **Models**: capability toggles (vision, reasoning) when adding custom models with upsert and live caps refresh
- **CLI tools**: support saving and managing custom API key presets
- **Quota**: add usage and rate-limit tracking for Groq via `x-ratelimit-*` headers
- **i18n**: complete Indonesian translation (1391 keys)
## Fixes
- **Security**: close SSRF guard bypasses in `ssrfGuard.js` (alternate IPv6 encodings, hostname trailing dots, wildcard DNS resolution check, safe redirect handling) (#3714)
- **Model markers**: strip the `[1m]` context marker Claude Code appends to model names (`claude-opus-5[1m]`) preventing model resolution failures (#3690)
- **Claude**: drop `server_tool_use` blocks carrying foreign IDs to avoid Anthropic 400 rejections; never anchor cache breakpoints on `defer_loading` tools (#3567)
- **Antigravity**: strike-break optimistic quota readings that keep 429ing by blocking the connection+model pair for 15m after 3 strikes (#3681); preserve client identity on model catalog requests (#3414)
- **Auth**: protect root `/responses` rewrite requiring API key validation in dashboardGuard
- **Chat & Docker**: return 503 Service Unavailable when all credentials are rate-limited; explicitly bundle `node-machine-id` into standalone Docker runtime image
- **OpenCode**: route Muse Spark models to `/zen/v1/responses` and declare vision support; filter inactive free model
- **Kiro**: preserve inline images as OpenAI-compatible `image_url` parts in OpenAI MITM; remove redundant top-level `systemPrompt` from payload
- **Usage**: read Responses-shape `cached_tokens` in `extractUsageFromResponse` for non-streaming traffic
- **Models**: support single model lookup with provider-prefixed IDs (e.g. `cc/claude-sonnet-5`)
- **Translator**: route Gemini thinking through `reasoning_effort` on OpenAI-compatible wire; convert `prefixItems` and ensure array items in Gemini schema sanitizer
- **UI**: apply persisted theme before first paint to prevent flash on reload; translate combo vision adapter label
Anthropic gates newly released models (e.g. claude-fable-5-1) to Claude
Code >= 2.1.251; the spoofed 2.1.92 client got HTTP 400 on every request.
Bump User-Agent + billing-header version to 2.1.258 and refresh the
providers baseline snapshot.
Co-Authored-By: Claude Code <noreply@anthropic.com>
- AddCustomModelModal lets users pick vision/reasoning caps when adding a model
- POST /api/models/custom whitelists caps to booleans
- aliasRepo.addCustomModel upserts — re-adding updates caps/name in place
- /api/models includes custom llm models with stored caps overriding the heuristic
- useModelCaps refetches on customModelChanged instead of trusting a stale cache
Co-Authored-By: Claude Code <noreply@anthropic.com>
## Features
- **Search**: new web search providers — Antigravity (Google Search grounding
on the existing OAuth account pool, citations keyed and merged by URL) and
Xquik (X search with `x-api-key` auth, cursor pagination, credit-based
usage), both on `POST /v1/search`. Based on #3437 by @Nautilaceae
- **Search**: ollama-search and zai-search borrow a chat provider's API key
instead of requiring their own connection, driven by a new
`credentialFallback` registry field. zai-search later folded into the `glm`
provider itself so the web search page shows the shared connection
- **Models**: daily background sync of model capabilities from models.dev —
modalities keyed by model id (majority of sources must declare one),
context/output limits keyed by provider + model, strictly additive and
sitting below the hand-written tables. ETag + mtime cache, 60s startup
delay, `MODEL_CATALOG_SYNC=off` to disable
- **Models**: add GLM-5.3-Flash (1M context, natively multimodal), DeepSeek
V4 Vision, Grok 4.5/4.6 (500k context); correct glm-4.6v/4.5v video input
and output limits, backfill glm-4.6v on glm-cn
- **Usage**: show the Zed plan quota on the dashboard — plan, edit
predictions, hosted model requests and billing-cycle reset; unlimited rows
render as "N used · Unlimited"
- **Usage**: track GPT-5.3-Codex-Spark quota windows (spark_session /
spark_weekly) from the Codex usage response (#3431)
- **Antigravity**: quota-aware routing — on 409/429 fetch live quota for the
exact per-model resetAt and skip only the exhausted account/model pair;
report the earliest reset when every account is blocked (#3561)
- **Antigravity**: map image `size` to the aspect-ratio model suffix (-WxH);
add the Gemini 3.7 Flash tiers to MITM defaultModels so they show up in
the dashboard model-mapping table
- **Dashboard**: bulk import Grok CLI accounts from JSON — paste an array or
drag-drop multiple .json files, all OAuth connections created in a single
call, mirroring the codex flow
- **CLI tools**: endpoint presets shared across every tool card through one
live-resyncing store, instead of per-card localStorage copies that never
saw each other's saved endpoints
- **Token Saver**: configurable compression timeout (`headroomTimeoutMs`) —
the fixed 3000 ms made busy machines time out and send inconsistently
compressed bodies, hurting prompt caching
- **i18n**: pt-BR expanded to 1132 terms
## Fixes
- **Stream**: record usage when a client closes on the terminal event — the
Responses API has no [DONE] sentinel, so codex closed the socket on
`response.completed` and cancelled the reader before flush() ran its usage
side effects; the tail now lives in a once-guarded finalizeStream(). Also
stop logging a disconnect for every completed Responses call
- **Stream**: parse the trailing NDJSON line an Ollama stream leaves behind
without a closing newline — the final chunk carrying `done_reason` and the
token counts was dropped
- **Session**: read the Claude Code session id from the
`x-claude-code-session-id` header — `metadata.user_id` is dropped by
Responses translation, splitting one conversation across several
`prompt_cache_key` values and missing the upstream prefix cache
- **Usage**: preserve nested `cached_tokens` — the top-level-only read
persisted `cached_tokens: 0` for every Responses-format provider (codex,
grok-cli, …), billing cache hits at the full input rate
- **Usage**: GLM quotas accept CREDIT_LIMIT plans and multi-interval windows
(5h session / 7d weekly) instead of overwriting a single "session" key
- **Models**: the catalog sync no longer erases its own output — deltas were
measured against the previous run's writes (the second run cut `providers`
from 20 entries to 5); one vote per provider in the modality tally, ETag
restored from file on startup, and the worker thread dropped after the
bundler rewrote its path into a module-not-found error
- **Executor**: CommandCode returns errors as a `type:"error"` event inside
an HTTP 200 NDJSON stream — peek the first events before committing, abort
and return a real 4xx/5xx so combo/account fallback triggers instead of
streaming the error text as content
- **Search**: scope failure locks on the credential-fallback path — a failing
search locked `modelLock___all` and took the shared glm key offline for
chat as well; locks are now attributed to the connection's owner and
scoped to `websearch:<provider>`
- **Providers**: connection tests get a 15s AbortSignal timeout instead of
hanging and exhausting the browser socket pool; guard undefined provider
names on the providers page
- **Antigravity**: sanitize competing-client branding via a config-driven
rule table (Zed's Claude-agent prompt, opencode → antigravity) — upstream
answers 429 Quota Exhausted. Applied in the executor so the shared
openai-to-gemini translator leaves gemini/vertex/zed untouched
- **MiniMax**: preserve images on the sourceFormat-matched OpenAI transport
— MiniMax-M3 resolved a Claude-shaped body posted to the OpenAI endpoint,
silently dropping `image_url` blocks (#3418)
- **Claude**: decloak tool names in same-format streaming passthrough —
OAuth-cloaked names (CLAUDE_TOOL_SUFFIX) leaked to the client and every
tool call was rejected as unknown
- **Tools**: default a missing `tools[].type` to "custom" on Claude-format
requests — strict Anthropic-compatible gateways (MiniMax) reject the
request with 400 otherwise
- **Translator**: zai thinkingFormat sends the top-level `reasoning_effort`
object GLM-5.2+ requires — every GLM-5.x request ran at the model default
(max); gated on GLM-5.2+ since older GLM does not read it (#2721)
- **RTK**: system prompt injection matches each target wire format
(Chat/Responses/Claude/Gemini/Kiro) and is exact-idempotent across retries,
so distinct prompts sharing a long prefix are no longer collapsed (#3202).
Also set the diagnostic before the silent null return on Responses
translation failure so the panel is no longer blank
- **OpenCode**: route muse-spark through /zen/v1/responses (it 500s on
chat/completions), normalizing the Chat fields the Responses API rejects
and clamping max/ultra effort to xhigh
- **CLI**: install better-sqlite3 without build tools on Node 22+ (N-API
13.0.3 ships per-platform prebuilds, `--ignore-scripts` skips the implicit
node-gyp build); Node < 22 stays on 12.6.2, working installs untouched
- **CLI tools**: send the API key Codex actually reads —
`[model_providers.9router.http_headers]` instead of auth.json (which left
every request 401 and clobbered an existing ChatGPT login); subagent model
moved to `agents.default_subagent_model`
- **OAuth**: refresh Cline tokens with the extension JSON contract
- **Dashboard**: clamp the API key mask length — keys shorter than 8 chars
threw RangeError and crashed the media-provider detail page
- **UI**: wait for the Material Symbols font itself before revealing icons —
`document.fonts.ready` resolved before the 4MB woff2 even started loading,
leaving icons blank until a second load
The committed snapshot had drifted from the registry: five providers
mismatched on a plain checkout and alitp-intl was missing entirely.
Remove it so the suite regenerates from the current registry.
Claude Code carries the session in metadata.user_id, which the Responses
API translation drops before the executor resolves a cache session. The
request then fell through to the assistant-text hash and the per-connection
fallback, so one conversation was split across several prompt_cache_key
values and the upstream prefix cache kept missing.
Fall back to the x-claude-code-session-id header, which survives every
translation. The body stays authoritative when both are present.
Two problems on the credentialFallback path, where a search provider
borrows a chat provider's connection:
- the lock was attributed to the search provider id, but the connection
belongs to the chat provider, so markAccountUnavailable looked it up
under the wrong provider and read a stale backoffLevel
- with no model argument the lock key is `modelLock___all`, which
isModelLockActive treats as blocking every model — one failing search
would have taken the shared glm key offline for chat as well
Attribute the lock to the provider that owns the connection, and scope
it to `websearch:<provider>`, passed to getProviderCredentials too so
the lock is read back under the same key.
The separate zai-search entry showed "No connections" on the web search
page because credentials live on the `glm` connection, not on it. Every
other provider that does both chat and search (antigravity, kimi, xai,
gemini) declares webSearch on the provider itself, so do the same here.
- glm gains serviceKinds ["llm", "webSearch"] and the MCP searchConfig
- the request builder / normalizer move from "zai-search" to "glm"
- drop the zai-search registry entry and its svg logo, which also
removes the only need for svg logo support in getProviderIconSrc
ollama-search keeps its own entry and credentialFallback: its search
endpoint is unrelated to the ollama chat transport.
Register two web search providers that reuse an existing chat provider's
API key instead of requiring their own connection:
- ollama-search (POST ollama.com/api/web_search) reuses the `ollama` key
- zai-search (POST api.z.ai MCP web_search_prime) reuses the `glm` key
A new `credentialFallback` registry field drives this: when a search
provider has no connection of its own, the search handler falls back to
the linked chat provider's credentials.
Also teach getProviderIconSrc to serve .svg logos for providers that
ship vector art.
`document.fonts.ready` resolved before the 4MB Material Symbols woff2 even
started loading — it runs in <head>, ahead of any element that would trigger
the lazy fetch. The `fonts-loaded` class landed early, so icons rendered
blank until a second load served the font from disk cache.
Load the face explicitly and swap visibility for opacity, with a 3s fallback
so icons never stay hidden if the font fails.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route POST /v1/search with provider "antigravity" through Google Search
grounding on v1internal:generateContent, using the existing Antigravity
OAuth account pool. Grounding chunks become citations with the grounded
sentence as snippet and its surrounding answer text as content.
Upstream repeats a source across chunks, so citations are keyed by URL
and their snippets merged. A missing projectId is reported up front —
upstream answers a fabricated or absent project with a misleading
"no valid license" 403.
Based on the approach in #3437 by @Nautilaceae.
Each card kept its own copy of the localStorage preset logic inside
BaseUrlSelect, so an endpoint saved on one card was invisible to the
others until a reload, and a URL typed into the custom field was
forgotten the moment the card collapsed.
Move the store into cliEndpointPresets.js and publish a change event
so open cards resync live. Applying settings now remembers the
endpoint unless it matches a built-in option, and each card passes
its configured URL as currentUrl so BaseUrlSelect can preselect the
matching preset instead of always falling back to 127.0.0.1.
Deleting a preset falls back to the first real option rather than
clearing the field.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Responses-API clients (codex, droid) close the socket on
response.completed because the protocol has no [DONE] sentinel, so
every successful request printed "⚡ DISCONNECT: ResponseAborted"
after its own "📊 done" line. Keep the dbg("CTRL", …) trace and drop
the console line; ABORTED and ERROR still print.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Responses API has no [DONE] sentinel, so codex closes the socket
as soon as response.completed arrives. That cancels the reader before
flush() runs — and flush() held every usage side effect, so a fully
successful request logged nothing: no 📊 done line, no token stats,
no request detail.
Extract that tail into a once-guarded finalizeStream() and also call
it right after the terminal event is forwarded, in both passthrough
and translate mode. flush() still calls it; the guard makes the
second call a no-op. Streams that end normally are unaffected, and a
terminal event carrying no usage falls through to the existing
estimate/null path rather than blocking.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex only authenticates a custom model provider from env_key,
http_headers, env_http_headers or a token command — auth.json is
read solely by the built-in openai provider. Writing OPENAI_API_KEY
there left every request unauthenticated (401 Missing API key) while
clobbering an existing ChatGPT login.
Put the key in [model_providers.9router.http_headers] instead, and
drop the auth.json write. Also move the subagent model to the
agents.default_subagent_model scalar: agents.<role> now declares a
custom role and requires a description, so the old [agents.subagent]
table was discarded with a startup warning. DELETE still clears
auth.json to repair machines configured by the previous version.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
collectEntries() computed each model's "current" capabilities with the
previous catalog still installed, so every delta was measured against the
last one. An upstream value that still agreed with what we had written
looked like no change and was dropped: the second run cut `providers`
from 20 entries to 5, taking glm-5.3's 1M context correction with it.
The baseline has to be the hand-written tables alone, so the reader is
detached for the snapshot and restored in a finally — a mid-sync failure
must not leave capabilities.js without it.
Two smaller corrections:
- One vote per provider in the modality tally. Ids that normalize to the
same model (claude-opus-4-thinking:1024, :8192, :32768 …) were each
counted, giving nano-gpt five votes where other gateways had one. No
model's result actually flipped — the variants agree with each other —
but the majority rule only means something if the denominator does.
- Restore the etag from the file on startup. It lived only in module
state, so every restart re-downloaded 4.3MB to be told nothing changed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The worker resolved its own path through import.meta.url, which the
bundler rewrites — so the running server looked for the file at a path
that does not exist there:
[modelCatalog] sync failed: Cannot find module
'/Users/Working/router4/9router/src/lib/modelCatalog/worker.js'
It was guarding against a 23ms JSON.parse that runs once a day, 60s after
boot. Inlining it into sync.js costs that 23ms on an otherwise idle tick
and removes both the failure mode and a whole file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Capability tables are hand-maintained, so a model gains vision or a wider
context only when someone notices and edits the file. This adds a daily
sync that fills the gap for models already in the registry.
How it decides:
- Modalities (vision/pdf/audio/video) belong to the MODEL — every gateway
serving glm-5.3-flash serves the same weights — so they are keyed by
model id and shared. A majority of sources must declare one, which keeps
out lone mis-declarations: minimax-m2.5 (1 of 45), glm-4.7 (1 of 44) and
gpt-oss-120b (2 of 76) are text-only despite a reseller claiming vision.
- Context/output limits belong to the GATEWAY — each truncates differently
(glm-5 ships as 202752/16384 on one host and 204800/131072 on another) —
so they are keyed by provider + model and only the matching provider's
own numbers are trusted.
Both layers are strictly additive and sit BELOW the hand-written tables,
which short-circuit first. A capability already true stays true.
Mechanics: worker thread (the 4MB parse would block the loop ~20ms),
ETag so an unchanged catalog costs one empty request, 60s startup delay,
30min backoff on failure, MODEL_CATALOG_SYNC=off to disable. Only the
~57KB delta is kept; lookups cost ~0.1us via an mtime-guarded cache.
capabilities.js is bundled into the browser through useModelCaps, so it
cannot import node:fs — the server injects the reader via
setCatalogSource() from instrumentation.
visionPatterns.js is the last resort: a model nobody has catalogued yet
still accepts images when its id says so (qwen3-vl-plus, glm-4.6v, llava),
with image-generation and embedding ids excluded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vendors shipped four multimodal models the registry did not carry:
- glm-5.3-flash — z.ai's first natively multimodal GLM-5, 1M context,
image + video + pdf input (glm, glm-cn, opencode-go)
- deepseek-v4-flash-vision-exp — image input at V4-Flash text parity,
1M context / 384k output (deepseek, opencode-go)
- grok-4.6, grok-4.5 — 500k context; 4.6 has no text output limit (xai)
Capabilities needed hand entries because the existing globs mis-matched:
*glm-5* and *deepseek-v4* carry no vision, and *grok-4* would have capped
grok-4.6 at 256k instead of 500k. The grok-4.6 pattern sits above the
generic *grok-4* so it wins the first-match lookup.
Also corrects glm-4.6v / glm-4.5v, which were missing video input and
declared no maxOutput, and backfills glm-4.6v on glm-cn — zhipuai serves
it and the sibling provider already listed it.
tests/unit/opencode-go-models.test.js pins the opencode-go model list, so
its expected array moves with the registry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Features
- **Auth**: native SAML 2.0 SSO alongside OIDC — AuthnRequest generation, ACS
assertion handling, SP metadata export, admin config test, replay-protected
via a `saml_state` cookie matched against `InResponseTo`
- **Providers**: add Alibaba Token Plan (`token-plan.ap-southeast-1`) — the
fourth Alibaba key type, Singapore-only and OpenAI-compatible transport only
- **Providers**: add `glm-5.3` to GLM Coding and GLM (China)
- **Providers**: Kimchi accepts API keys as well as OAuth (dual auth), with a
working Test Connection for both modes
- **Antigravity**: add Gemini 3.7 Flash and its tiered high/medium/low variants
(also in the Gemini registry) with pricing and quota tracking
- **TTS**: add Fish Audio — model id travels in an HTTP `model` header, voice
is a `reference_id` (preset or cloned voice model)
- **OpenCode-Go**: route by request format via declared transports instead of
forcing every client into `/messages` — Codex/OpenAI clients no longer pay a
lossy Responses→OpenAI→Claude double translation. Per-model `supportedFormats`
guard; the bespoke executor is gone (its shared `_lastModel` cache could cross
auth headers between concurrent requests)
- **Usage**: dedup + cache Claude quota calls (120s TTL keyed by access token,
in-flight promise dedup, last-good read on soft failure) to stop multiple
tabs tripping 429; manual refresh (↻) sends `force=1` to bypass the cache
## Fixes
- **Docker**: ship `sql.js` in the image so the pure-JS DB fallback can start —
file tracing carried the package's JS without `dist/sql-wasm.wasm`, so a
container with no native driver aborted with ENOENT and never got a database
(#3248)
- **Usage**: read Gemini `usageMetadata` out of the antigravity `{ response }`
envelope — every non-streaming antigravity request logged `IN 0 | OUT 0`
(#3260)
- **Claude**: re-anchor passthrough cache breakpoints — the client's own
`cache_control` markers point at pre-normalization offsets, so the tail was
re-cached every request. Last system block and last tool pinned at 1h TTL,
last assistant turn at 5m, mid-conversation system messages folded into the
neighbouring user turn instead of hoisted into `body.system`
- **Combos**: detect images from Hermes and attachment payloads (`images[]`,
`experimental_attachments`, message-level `image_url`/`audio_url`, inline
`data:` URIs) so the Vision Adapter auto-switch fires for Hermes/Ollama/
Vercel AI SDK shapes
- **Kiro**: intercept chat via `x-amz-target` — Kiro IDE 1.0.228+ moved
`GenerateAssistantResponse` to `POST /` + header, bypassing MITM. Also emit
the now-mandatory initial-response frame and map the `auto` model slot
- **Kiro**: report real output tokens and stop discarding usable turns
- **Qoder**: detect billing blocks at stream start and return a synthetic 403
so combo/account fallback triggers instead of leaking the error into chat
- **Antigravity**: strip competitive system prompts (Zed IDE's Claude-agent
prompt) that Antigravity flags with a 429 Quota Exhausted
- **OpenCode**: send the official client fingerprint on free-tier requests so
the Console stops classifying traffic as unidentified and rate-limiting it;
session id resolves conversation-stable to preserve prompt caching
- **Responses**: don't close the message on an empty `tool_calls` array — some
providers attach one to every chunk, and the truthy check ended the message
on the first content token (#3234)
- **Translator**: preserve `prompt_cache_key` when converting chat to responses
- **Models**: expose snake_case token limits on `/v1/models`
- **Combos**: strip `stream_options` from the Fusion panel fan-out to avoid a
DeepSeek 400 (#3024); raise the dashboard model-test probe budget to 1024 and
soft-pass reasoning-only responses (#3010)
- **Headroom**: the toggle reflects the `headroomEnabled` setting even when the
proxy is down — it previously showed OFF while the engine kept calling
`/v1/compress`; proxy status stays visible via the status chip
- **Hermes**: add the `api_key` parameter to the model block in YAML config
- **Providers**: add llm7 to provider test support
## Docs
- **i18n**: add Spanish, French, and Brazilian Portuguese README translations
## Security
- **Real IP**: `x-9r-real-ip` and the Host fallback were trusted from
client-controlled headers whenever `custom-server.js` was not in the request
path (`npm run start`, `start:bun`), letting a remote caller pose as local to
skip API key auth and reach `LOCAL_ONLY_PATHS` (`/api/mcp/*`,
`/api/tunnel/enable`, `/api/auth/reset-password`). The server now stamps a
per-process `x-9r-peer-token` on every request it sanitizes and only trusts
`x-9r-real-ip` behind it — falling back to Host in development and failing
closed in production (GHSA-pjm4-8fpg-f9p6). Also fixes IPv6 loopback
detection (`::1`, `::ffff:127.0.0.1`) and routes `npm run start` /
`start:bun` through `custom-server.js`
- **Search**: `resolveBaseUrl()` rejects client-supplied non-public baseUrls
(SSRF guard on `/v1/search`)
- **Login**: fresh-install remote login with the default password returns 403
without issuing a JWT
- **Usage**: `/api/usage/request-details` redacts request/response payloads
Passthrough kept the client's own cache_control markers, which point at
pre-normalization offsets. Once normalize/dedupe reshaped system and tools,
the breakpoints landed mid-array and the tail was re-cached every request.
- Pin the last system block and last tool at ttl 1h (was the client's 5m)
- Anchor the last assistant turn at 5m, falling back to the final message
so a first turn still gets a breakpoint
- Fold mid-conversation system messages into the neighbouring user turn
instead of hoisting them into body.system, where the volatile token
counters invalidated the prefix on every request
- Run the anchoring after every token saver, at the final body
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Manual refresh (↻) sends ?force=1 so it bypasses the Claude quota cache (dedup + TTL) added in cd4003bc. Auto-refresh and multi-tab stays cached, so Anthropic's usage endpoint is no longer hammered.
Co-Authored-By: Claude <noreply@anthropic.com>
Multiple tabs/accounts/auto-refresh funneled straight to Anthropic and tripped 429. Add a 120s TTL cache keyed by access token with in-flight promise dedup, serve the last good read on soft failure, and thread a force flag through getUsageForProvider for manual refresh. Also lower the dashboard poll cadence (180s to 600s) and stable group-by-provider so connection order stops jumping.
Co-Authored-By: Claude <noreply@anthropic.com>
Toggle was checked={headroomEnabled && headroomRunning} and disabled when the proxy was down, so a downed proxy showed OFF while headroomEnabled stayed true in the DB. The engine only checks headroomEnabled, so it kept calling /v1/compress. Toggle now reflects the user setting; proxy up/down stays visible via the status chip.
Co-Authored-By: Claude <noreply@anthropic.com>
## Features
- **Providers**: add TokenRouter (300+ models via OpenAI-compatible gateway) with
exact per-model pricing for 110 models and `reasoning_effort` thinking config
- **Providers**: add Self-hosted STT / TTS / Embedding — point 9Router at your own
OpenAI-compatible speech and embedding servers (whisper.cpp, faster-whisper,
Kokoro-FastAPI, llama-server, vLLM, Infinity). Unlike the named cloud providers
these read `baseUrl` per connection, so one provider can front several machines
- **Combos**: default-enable vision/audio capacity adapter (auto-routes to a
vision/audio-capable model when the target lacks that capability, falling back
to `oc/mimo-v2.5-free`), wired into chat handler routing
- **Endpoint**: auto-provision a "Default Key" for first-time users so `/v1`
works without a manual dashboard step
- **Codex**: support GPT-5.6 Max/Ultra reasoning-level overrides (cx/ routes only)
- **Qoder**: support PAT (Personal Access Token) connections end-to-end, alongside
OAuth device flow
- **CLI tools**: add OpenDesign (manalkaff/opendesign) support
- **Headroom**: report effective payload savings (tool schema/history bytes broken
out, byte-savings % reflects actual outbound reduction)
- **Ollama**: Cloud quota tracker (session + weekly) + proactive background OAuth
token refresh scheduler for all providers
## Fixes
- **Providers**: remove Qwen (OAuth flow stopped working reliably)
- **Passthrough**: detect codex-tui/Codex Desktop as native Codex client — they
were falling through to the translator and losing fields like `reasoning.summary`
- **OAuth**: scope antigravity header fixes to loadCodeAssist/onboardUser only
- **OAuth**: keep `open` external in the build so xAI/Grok token refresh works on
Windows
- **OAuth**: declare missing `searchParams` in register-session handler (was a
500 instead of JSON on error)
- **DB**: `ENABLE_REQUEST_LOGS` env var now overrides the UI setting correctly;
observability defaults to off (opt-in)
- **Translator**: preserve Codex Responses Lite tool use across chat-native
OpenAI-compatible providers
- **Translator**: don't drop image-only user messages in `prepareClaudeRequest`
- **Translator**: drop JSON Schema keywords Gemini rejects (`uniqueItems`,
`contains`, `multipleOf`, `unevaluatedProperties`, `unevaluatedItems`,
`contentSchema`)
- **Claude**: remove global header cache that leaked one client's identity
headers onto another client/account sharing the server; gate `anthropic-beta`
by model instead
- **Antigravity**: drop retired Gemini 3.0 quota tiers, show Gemini 3.6 Flash
usage bars
- **Cloudflare AI**: declare API key authentication (dashboard showed "No
connections" despite an active key)
- **GitHub Copilot**: hold monthly-exhausted accounts until UTC month reset
instead of only cooling down 120s
- **CodeBuddy**: dodge Tencent CN content filter, add usage tracking, normalize
codebuddy-intl messages
- **Usage**: stop losing cached prompt tokens in the forced-SSE→JSON path
- **Grok CLI**: display the public subscription tier from the OAuth token claim
- **Providers**: count apikey connections for Ollama free-tier card; free-tier/
apikey providers without `authModes` now default to apikey (were treated
oauth-only)
- **Build**: include static/public assets in standalone output (login page hung
on 404s when run via PM2)
- **Server**: support IntelliJ IDEA OpenAI-compatible clients over HTTP (h2c
upgrade handling)
- **Auth**: redirect already-logged-in sessions away from `/login`
- **CLI tools**: enable Apply button for dynamic OpenAI/Anthropic-compatible
provider connections
- **CLI**: include complete API artifacts in the CLI package
- **TTS**: a bare self-hosted model name is the MODEL, not the voice — `kokoro`
was parsed as a voice against a default model, 404ing or synthesising with the
wrong one
endpoint that drops packets never returns headers, so the request previously
hung indefinitely
Google fingerprints User-Agent/Client-Metadata on loadCodeAssist and
onboardUser, silently refusing to provision a cloudaicompanionProject
when they don't match the real IDE. Split antigravity's headers out of
the shared gemini-cli constants instead of overwriting them, so the fix
doesn't touch gemini-cli or any other provider.
Inspired by #3000 (thanks @stoXmod for flagging the resource-exhausted
issue), rewritten to keep gemini-cli untouched.
Sync alias-baseline.json and providers-baseline.json with the current
registry (poolside, tokenrouter, selfhosted-* providers already added;
stale claudeOverlay hook and Kiro X-Amz-Target header already removed).
- Endpoint page auto-creates a "Default Key" when no keys exist yet,
so /v1 works out of the box without a manual dashboard step
- Show/copy key buttons stay visible instead of opacity-0 by default
Qwen OAuth flow (portal.qwen.ai) stopped working reliably; drop the
executor, registry entry, OAuth provider/service, token refresh
profile, usage handler, and related test coverage and baselines.
- detectRequiredCapabilities: infer audioInput/videoInput from block
type and embedded mime, not just vision/pdf
- handleChat / handleSingleModelChat: augment combo and single-model
routing with capacity-adapter models when the target lacks a
required capability, wrapped with history stripping for the
adapter model's context window
- Enable vision + audioInput capacity-adapter pools by default for new
and existing users (mergeWithDefaults backward-compat)
- Fall back to oc/mimo-v2.5-free when an enabled pool has no models
configured, both in the backend resolver and the combos UI (auto
refill on removing the last model from a pool)
- Hide PDF/Video from the Vision Adapter UI (PDF never implemented,
Video lacks translator support) while keeping the settings shape
- Exclude combos from the model picker when opened from the Vision
Adapter section
- mimo-v2.5 registry entry now declares audioInput/videoInput
- Simplify combo strategy and Vision Adapter descriptions
detectClientTool only matched the legacy "codex-cli" User-Agent, so the
current codex-tui CLI and Codex Desktop (UA "Codex Desktop", originator
"codex_work_desktop") fell through to null and lost native passthrough —
their requests got re-translated, stripping/overwriting fields like
reasoning.summary instead of forwarding the client body as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The global claudeHeaderCache singleton overlaid the last-seen Claude Code
client's identity headers onto every subsequent request, leaking one
client's headers (anthropic-beta, user-agent, x-stainless-*, etc.) onto
another client/account sharing the same server. Removed the singleton and
the claudeOverlay hook entirely, falling back to static per-provider
headers. anthropic-beta is now computed per-request from the requested
model, gating heavy-agent flags (advanced-tool-use, effort) to
opus/sonnet only.
PAT-to-job-token exchange was duplicated between the executor and the model service, each with its own cache. Consolidate into qoderModels.js and have the executor import it.
Also add a qoder case to the API-key validate route - the generic OpenAI-compat probe cannot validate a PAT (needs job-token exchange + COSY signing first), so bulk-add always reported unknown for qoder keys.
Remove "Ported from OmniRoute" and cockpit-tools attribution comments.
User-Agent strings and README/landing credits are left intact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
openrouter, nvidia, gemini lack authModes, so dualAuthTypes on the
providers page defaulted to "oauth" and their apikey connections showed
as "No connections" on the freeTier card.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Free-tier cards (e.g. kimchi, oauth-only) hardcoded "apikey" for stats and
toggle, so oauth connections were invisible on /dashboard/providers despite
showing on the detail page. Use dualAuthTypes per provider instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the fixed-table quota layout with flex rows that shrink cleanly,
keep the hidden-quota chip row from overflowing, and use thin mac-like
scrollbars app-wide.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a context-window selector on ClaudeToolCard that writes
CLAUDE_CODE_MAX_CONTEXT_TOKENS into settings.json (nudged 2K under the
labeled cap), and clear it on reset/default.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Update model lists and context lengths across free/apikey providers,
move bazaarlink, kilo-gateway, and kimchi into freeTier, demote llm7 to
apikey, and hide bluesminds, sambanova, zed, and mimo-free.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Default to the full agent with built-in tools, expose client function
tools as an MCP server, surface tool calls as OpenAI tool_use, resolve
workspace cwd from the request, and bump context windows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Exchange Personal Access Tokens for short-lived job tokens, close the
SSE stream on terminal frames so non-streaming clients do not hang,
re-enable OAuth plus API-key auth modes, and replace the model catalog
with the current Qoder aliases.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Decode GetGrokCreditsConfig frames when REST billing returns empty
caps, so SuperGrok weekly quota shows in the usage dashboard.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wire /v1/usages for Kimi (OAuth + API key) and balance API for DeepSeek,
flag both providers with usage/usageApikey, and normalize their quotas
in the dashboard ProviderLimits parser.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use w-full instead of min-w-0/flex-1 + overflow-x-auto so the hidden
quota chips wrap cleanly instead of stretching the row.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wire Devin CLI as a routed provider that spawns the local `devin acp`
binary. Add the DevinCliExecutor, register it in the executor map, expose
its status through the cli-tools batch endpoint and devin-settings route,
and document setup in cliTools constants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Register 9 new upstream providers with logos and update the auto-generated
registry index. Refresh providers/alias baselines and extend the alias
token allowlist so verify-alias stays green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rotating-RT providers (xAI/grok-cli) issue a new refresh_token on every
refresh; mutate credentials in-place so refreshWithRetry reuses the fresh
RT instead of the already-consumed one.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8b9cac1 swapped alicode-intl to the DashScope compatible-mode endpoint to
fix#2591 for standard DashScope keys, but that broke Coding Plan keys
(sk-sp-...) which only work on coding-intl.dashscope.aliyuncs.com. The two
key types use two different hosts and are not interchangeable.
- alicode-intl: revert to coding-intl endpoint (Coding Plan keys)
- alims-intl: new provider for dashscope-intl/compatible-mode (standard keys)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Share one /api/models fetch via useModelCaps cache, mount ModelSelectModal
only when open, stop double fetchModelAliases on CLI tool cards, and resolve
provider icons through a session 404 cache with missing PNGs + loading=lazy.
Also include Claude Exa MCP toggle (claude-settings + ClaudeToolCard) that
was already in the working tree.
Co-authored-by: Cursor <cursoragent@cursor.com>