CHANGELOG.md
Rama release/v3.8.54 · réplica de lectura del repositorio original.
# Changelog
## [Unreleased]
### ✨ New Features
- **feat(dashboard):** adaptive context-budget dial on the compression settings panel — mode (`off` / `floor` / `replace-autotrigger`) and policy (`reserve-output` / `percentage` / `absolute`) persist via `PUT /api/settings/compression` `contextBudget`. Completes the dashboard half of #7005 (API + DB already shipped in #7183).
- **feat(sse): STRICT_ZERO_COST** — opt-in, off-by-default `freeAccessPolicy: "strict"` setting
that hard-verifies every auto-combo candidate against live quota state and per-connection
economic safety before it can be dispatched, going beyond `hidePaidModels`'s static catalog
check. Adds curated `hardStopGuaranteed` metadata to `FREE_MODEL_BUDGETS`, a short-TTL quota
cache reusing `getUsageForProvider()`, and a connection-safety guarantee: a candidate backed
by multiple accounts has its `allowedConnectionIds` narrowed to exactly the connections
independently verified `SAFE`, so dispatch can never use an unverified account. An
`excludeTosAvoid` guard (default `false`) is available separately for contractual risk. See
`docs/routing/STRICT_ZERO_COST.md`.
---
- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342)
- **feat(credential-health):** pace the credential health sweep per connection via `provider_connections.healthCheckInterval` (minutes, 0 = never), with `CREDENTIAL_HEALTH_CHECK_INTERVAL` as the global default ([#8443](https://github.com/diegosouzapw/OmniRoute/issues/8443))
- **behavior change:** `healthCheckInterval` is a shared column — it paces both the OAuth token refresh and the credential health sweep, and `0` disables both. The connection editor defaults it to 60, so configured OAuth connections are now credential-checked at 60min instead of the previous ~10min (aligned with the probe-volume goal of #8443)
- **feat(providers):** publish Poolside's Laguna Preview catalog statically — `poolside/laguna-xs-2.1` and `poolside/laguna-s-2.1` (262144 context, 32768 max completion, tools + reasoning, text-only), so the models are routable and visible before a key is configured instead of only after live discovery. Pins the catalog form of the XS id against the `laguna-xs.2` variant carried by third-party listings. ([#9085](https://github.com/diegosouzapw/OmniRoute/issues/9085))
- feat(modality-bridge): bridge Chat and Responses video parts through a strict trusted-loopback, quota-bounded FFmpeg broker; enforce HTTPS redirects/SSRF plus format, protocol, stream, pixel, frame, 50 MiB broker/remote, 36 MiB inline, and 120-second limits; propagate caller aborts; preserve the actual successful fallback model through cache/meta/headers; expose sampled latency and honest success telemetry; and ship the localized Video settings UI (#9760)
- **feat(radar):** Persist local model display-name/enabled overrides and hide/restore tombstones, with authenticated catalog controls and feed safety precedence ([#9830](https://github.com/diegosouzapw/OmniRoute/pull/9830))
- **feat(radar):** add curated-family combo suggestions, a guided combo page, and the read-only Radar MCP catalog tool ([#9836](https://github.com/diegosouzapw/OmniRoute/pull/9836))
- **feat(radar):** add a signed live offers feed and supporter offers dashboard ([#9912](https://github.com/diegosouzapw/OmniRoute/pull/9912))
- **feat(radar):** add signed Intel insights, supporter recognition, and local Radar CLI commands ([#9923](https://github.com/diegosouzapw/OmniRoute/pull/9923))
- **feat(radar):** add a localized public news feed and dismissible dashboard launch banner, with the Radar announcement staged inactive for a separately authorized launch ([#9926](https://github.com/diegosouzapw/OmniRoute/pull/9926))
- **feat(admission):** add lane-aware admission probes for combo/fusion/chaos fan-out (fail-open, queueing disabled), an env-wins `OMNIROUTE_CHAT_VIRTUAL_LANES` activation flag applied at boot, and adaptive-lane visibility in the `omniroute_get_health` MCP tool (related to #9654)
- **docs(mcp):** complete the MCP server README tool reference so the `schemas/` catalog is fully covered (agent-skills, oneproxy, web, tool-search, combo/routing, pricing and DB-health tools were previously only discoverable via `omniroute_tool_search`)
- **feat(cli):** container-aware auto-config — `setup-*`, `omniroute configure`, `omniroute config set` and the CLI-tool config APIs now refuse to write into a containerised OmniRoute's ephemeral home (CLI exits `2`, API returns `422` with `containerEphemeralTarget`) and point at the host-CLI or bind-mount setup instead; `--allow-container-write` / `OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true` opt back in. Also fixes `CLI_CONFIG_HOME` so the Compose `host` profile's `/host-home` bind mounts are honoured instead of silently falling back to the container home. (#10057)
- feat(dashboard): opt-in `DASHBOARD_ALLOW_EMBED=vscode` relaxes CSP `frame-ancestors` to `'self' vscode-webview:` and drops `X-Frame-Options` for HTML pages only, so the dashboard renders inside the VS Code Simple Browser (OmniCopilot). Default posture unchanged — API routes stay unframable (#10273)
- **feat(resilience):** warn when `/healthz` is served under event-loop lag ≥200ms so a slow 200 is visible as sick, not healthy ([#10303](https://github.com/diegosouzapw/OmniRoute/issues/10303))
- **feat(docker):** add `GET`/`HEAD` `/livez` as a process-alive probe, distinct from `/healthz` readiness ([#10316](https://github.com/diegosouzapw/OmniRoute/issues/10316))
- feat(providers): add **Cloudflare AI Playground** as a No Auth provider (`cloudflare-playground`, alias `cfp`) — free anonymous chat over the reverse-engineered `cf_agent` WebSocket protocol (PartySocket transport, no account/API key/cookies) with GLM 5.2, Kimi K2.7 Code, DeepSeek V4 Pro, gpt-oss-120B, Llama 3.3 70B, Qwen2.5 Coder 32B and 14 more curated models. The executor drives a headless Chromium via Playwright (the WS upgrade is TLS-fingerprint-gated), translates the `cf_agent` frame stream into OpenAI SSE, and surfaces upstream rate limits (3021) as HTTP 429. Fixes #10389
- **feat(providers):** AI Horde accepts an optional registered API key and advertises only live image models that currently have workers ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))
- **fix(providers):** AI Horde Check validates keys via `/v2/find_user` instead of the unauthenticated OpenAI models list ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))
- **feat(audio):** proxy native ElevenLabs voices, text-to-speech, and speech-to-text HTTP routes through stored OmniRoute credentials, preserving query strings, multipart uploads, binary responses, and upstream errors (#10556).
- **feat(providers):** complete Jina AI as one credential pool — dashboard `jina-ai` / `jina-reader` share a token, `JINA_AI_API_KEY` is a real fallback, Test probes `GET https://api.jina.ai/v1/models` (embeddings fallback hits `jina-embeddings-v5-omni-small`), embed/rerank logs keep `connection_id`, catalog adds `jina-reranker-v3.5`, Omni v5 multimodal `{text}`/`{image}`/`{content}` docs pass through intact, and OmniRoute proxies classify / segment / `jina-search` (`s.jina.ai`). Reader stays a separate `r.jina.ai` card with an explicit label. Gemini Embedding 2 (`gemini/gemini-embedding-2`, alias `google/gemini-embedding-2`) uses dashboard `gemini` keys (or `GEMINI_API_KEY` / `GOOGLE_API_KEY` only when none exist), forwards native multimodal parts, and maps N OpenAI `input` items to N `:batchEmbedContents` vectors instead of one aggregated `:embedContent`. ([#10581](https://github.com/diegosouzapw/OmniRoute/pull/10581))
- **feat(providers):** accept `response_format=ogg` on `/v1/audio/speech` as an alias for the existing Opus/Ogg encoder ([#10587](https://github.com/diegosouzapw/OmniRoute/issues/10587))
- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`.
- **feat(settings):** add `autoDisableBannedScope` so permanent-ban auto-disable can target subscription/OAuth accounts only, leaving prepaid API keys in the routing pool ([#10617](https://github.com/diegosouzapw/OmniRoute/pull/10617))
- feat(server): emit systemd sd_notify READY/WATCHDOG/STOPPING (generated unit becomes Type=notify with WatchdogSec=180) so a frozen server process is killed and restarted by systemd instead of lingering undetected
- **feat(providers):** add the TabiToken NewAPI gateway (`tabitoken`) and teach the existing HCNSec entry (`hcnsec`) the three further protocols it actually serves. TabiToken leaves the NewAPI pricing endpoint public, so its catalog is read from the host rather than guessed: four Claude models, each reporting the Anthropic and OpenAI protocols. HCNSec shipped OpenAI-only; probing the host showed `/v1/messages`, `/v1/responses` and the Gemini `/v1beta` path all reach its token layer, so each is now declared as an alternate format — with its default format, base URL, auth scheme and regional catalog classification untouched. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil
- **feat(sse):** allow an alternate protocol to build its own upstream URL. `AlternateFormat` gained an optional `urlBuilder`, because the Gemini protocol carries the model inside the path (`{base}/{model}:generateContent`) and the existing `chatPath`/`urlSuffix` fields are constants that cannot express it. The route builder is extracted as `buildGeminiGenerateContentUrl` and shared with the native `gemini` provider so the two consumers cannot drift on the `?alt=sse` streaming suffix. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil
- **feat(call_logs):** persist the per-call error family in `call_logs.error_type` and expose a failure breakdown (`errorBreakdown`) in the usage analytics endpoint, reusing the existing production classifier ([#10670](https://github.com/diegosouzapw/OmniRoute/issues/10670))
- **feat(proxy):** the proxy-health sweep and `GET /api/settings/proxies/egress` now report an anonymous summary of egress-IP sharing — how many rotation groups share an egress IP and the largest number of accounts behind one IP — computed from persisted `proxy_logs` over a 24h window. No IPs and no account identities by default; `PROXY_LOG_INCLUDE_IPS=true` restores raw details. ([#10677](https://github.com/diegosouzapw/OmniRoute/issues/10677))
- **docs(guides):** OmniRoute now serves VS Code's **native Copilot Chat model picker** through the [OmniCopilot](https://github.com/diegosouzapw/OmniCopilot) extension ([Marketplace](https://marketplace.visualstudio.com/items?itemName=diegosouzapw.omnicopilot) · [Open VSX](https://open-vsx.org/extension/diegosouzapw/omnicopilot) — Cursor, Windsurf, VSCodium, Theia…) — no Copilot subscription needed since VS Code 1.122. New [`docs/guides/VSCODE-COPILOT.md`](docs/guides/VSCODE-COPILOT.md) covers setup, how the picker collapses the `dual`-prefix catalog via `GET /v1/models?prefix=alias`, and the **build-time** `DASHBOARD_ALLOW_EMBED=vscode` flag that renders the dashboard in an editor tab ([#10697](https://github.com/diegosouzapw/OmniRoute/pull/10697))
- **feat(docker):** `DASHBOARD_ALLOW_EMBED` is now a Docker build argument — `docker build --build-arg DASHBOARD_ALLOW_EMBED=vscode` produces an image whose dashboard renders inside the VS Code Simple Browser (OmniCopilot's `dashboardOpen: "editor"`). Previously the flag was only reachable from a source build: Docker silently drops a `--build-arg` with no matching `ARG`, so the operator got the default image and no error. Builder-stage only and empty by default — the runtime stages deliberately do not carry it, and the unframable default posture is unchanged ([#10701](https://github.com/diegosouzapw/OmniRoute/pull/10701))
- **feat(providers):** new `cursor-api` provider (card "Cursor API", alias `cua`): connect a Cursor user API key (`crsr_…`) and route `cursor-api/<model>` through the existing Cursor agent executor (the key is exchanged for a 1h session token and cached), plus a `/api/cursor-cli/*` passthrough so the Cursor CLI itself runs through OmniRoute (`CURSOR_API_ENDPOINT=http://<omniroute>/api/cursor-cli`, `CURSOR_API_KEY=<OmniRoute key>`) with every RPC attributed and logged. The IDE `cursor` provider is unchanged. (#10729)
- **feat(api):** `GET /api/health` now answers `{ status, timestamp }` without a key. Until now the path had no route, so the management-auth boundary answered first with a 401 — indistinguishable from a wrong key or an unknown route, which left Docker HEALTHCHECKs and Kubernetes probes unable to tell "down" from "misconfigured". Kept deliberately minimal: version, uptime and memory stay behind the authenticated `/api/monitoring/health` ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10771)).
- feat(routing): make Task-Aware Smart Routing's detection patterns operator-configurable via `settings.taskRouting.patternOverrides` (`PUT /api/settings/task-routing`) — the built-in patterns are English-only, so a non-English dashboard had no recourse short of turning detection off entirely; an override now replaces the pattern list for one task type without touching the rest (#10783)
- feat(api): accept PATCH on /api/combos/[id], the verb the OpenAPI spec already documents (#10869)
- **feat(sse):** add GLM-5.3 support (`glm-5.3`, `glm-5.3-high`, `glm-5.3-low`) across the z.ai first-party providers, mapping the upstream `reasoning_effort` request parameter to the existing 5.2 tier UX ([#10896](https://github.com/diegosouzapw/OmniRoute/pull/10896)) — thanks @phuongddx
- **feat(home):** add a live **Recent Requests** panel beside the home Provider Topology (polls `GET /api/usage/call-logs?excludeTests=1` every ~3s, gated by the topology appearance toggle + page visibility). `excludeTests` is now an allowlist of real provider inference (`/v1/%` or `/api/v1/%`), applied before `LIMIT`, so connection-test/model-sync/management rows can never leak into the feed ([#10897](https://github.com/diegosouzapw/OmniRoute/pull/10897), extracted from [#8450](https://github.com/diegosouzapw/OmniRoute/pull/8450)) — thanks @nguyenha935
- **feat(rankings):** free provider rankings now expose a `reliability` field (raw `testStatus`/`rateLimitedUntil` per connection plus a `healthy`/`degraded`/`down` state, reusing the `ProviderHealthState` vocabulary of the provider health matrix) when the configured/available filters are active — derived from already-loaded data, without touching the ranking order ([#10909](https://github.com/diegosouzapw/OmniRoute/pull/10909))
- `feat(resilience)`: when an allowlisted provider (opencode family) answers
429 classified `quota_exhausted` or `rate_limit_exceeded` and its free-tier
quota is bucketed by egress IP (#9611), every connection of that family
sharing the IP is cooled down together before the rotation tries them — one
guaranteed-failed upstream call per episode instead of N, on the combo path
as well. For the allowlisted family a 429 now cools the connection instead
of locking a single model. Exclusive allowlist, never terminal, best-effort
when the egress IP is unknown (#10920).
- **feat(rankings):** free provider rankings can now report what each provider actually served — `reliability.usage` (requests, successes, success rate over a window) behind the opt-in `withUsage`/`usageRange` query parameters, so a provider that answers every call with an error is no longer described as healthy ([#10926](https://github.com/diegosouzapw/OmniRoute/pull/10926))
- **feat(providers):** add Logfare as a free OpenAI-compatible provider — dashboard card with a Free badge and request-logging disclosure (every prompt/completion is logged for research; opt out at logfare.ai/consent), live model discovery from `https://logfare.ai/v1/models` (20 models, 11 chat-capable: kimi-k3, deepseek-v4-pro, glm-5.2, gpt-5.6-luna, minimax-m3…), full chat/streaming through the existing OpenAI-compatible path, the real Logfare logo on the card, and a listing in the free-tiers guide. ([#10987](https://github.com/diegosouzapw/OmniRoute/pull/10987))
- Run synchronous RTK and Caveman request compression in a bounded worker-thread pool, keeping
large `/v1/responses` compression heaps outside the HTTP isolate while preserving strict
fail-open behavior and per-engine telemetry.
- **feat(providers):** let operators declare per-provider error rules through `settings.providerErrorRules` instead of patching the catalog — an operator-supplied rule for a provider is consulted before the built-in `providerRuleRegistry`, receives the raw error text, and has its declared scope/cooldown/reason actually honored end to end, for any provider (declaring the rule is the opt-in — no extra allowlist entry needed). Matches are plain case-insensitive substrings (never RegExp) and bounded to 50 rules to keep the hot path safe ([#11104](https://github.com/diegosouzapw/OmniRoute/pull/11104))
- **feat(combo):** the shared per-request combo attempt budget is now operator-configurable via `maxGlobalAttempts` (combo config / `comboDefaults` cascade), instead of the hardcoded 30. Lower it to fail fast on a dead target pool, raise it for large combos; clamped to `[1, 200]` so an unbounded budget can never cause runaway background requests ([#11134](https://github.com/diegosouzapw/OmniRoute/issues/11134))
- **feat(api):** `/api/usage/om-usage` gains a structured form — `?format=json` returns the key's own usage as `ApiKeyUsageLimitStatus` + `UsageSnapshot` instead of `text/plain`. This is the surface a UI (the OmniCopilot panel) consumes to show a key holder their daily/weekly spend and quota reset. The route is self-service (the caller's own key, gated by `allowUsageCommand`), not the management surface; refusals come back as a discriminated `{ "allowed": false, "error": … }` so a UI can tell "not allowed" apart from "allowed but nothing cached yet". The endpoint was previously undocumented in `API_REFERENCE.md`; it now has a section ([#11190](https://github.com/diegosouzapw/OmniRoute/pull/11190))
- **feat(api):** `/api/usage/om-usage?format=json` now returns `providers[]` — every connection's quota snapshot, not just the single selected one — so a panel can render Codex / Claude / OpenCode side by side. The collector already gathered all of them; the single-pick `provider` field (kept) is a terminal presentation choice. Closes the per-connection gap from OmniCopilot #8 ([#11192](https://github.com/diegosouzapw/OmniRoute/pull/11192))
- **feat(providers):** allow overriding the rate-limit queue wait timeout (`maxWaitMs`) per connection, alongside the existing `rpm`/`tpm`/`tpd`/`minTime`/`maxConcurrent` overrides — a single slow provider no longer has to lower the global wait budget for every other provider (#11251)
- **feat(dashboard):** replace the hard Home → onboarding redirect with a dismissable first-run readiness card so returning users can stay on Home while new users still get a clear 4-step path ([#11282](https://github.com/diegosouzapw/OmniRoute/pull/11282))
- **feat(dashboard):** lead Traffic Inspector with a purpose-first header that separates "what happened" from "how it happened", so beginners can read request outcomes without drowning in protocol detail ([#11283](https://github.com/diegosouzapw/OmniRoute/pull/11283))
- **feat(dashboard):** add an Essentials sidebar preset that shows only the beginner core path (Home → Endpoints → API Keys → Providers → Health → Settings) while keeping Advanced tools reachable via Command Palette search ([#11286](https://github.com/diegosouzapw/OmniRoute/pull/11286))
- **feat(video bridge):** harden the optional drill-down cache substrate with exact-path broker policy, canonical principal/session/media isolation, independent retained-byte quotas, cancellation-safe commits, rejection of excess or non-canonical Base64 padding and non-JPEG/truncated media, warning-sensitive full JPEG canonicalization that strips trailing polyglot bytes, server-derived dimensions, and auditable derivation metadata; production tenant binding and multi-resolution selection remain follow-up work ([#11369](https://github.com/diegosouzapw/OmniRoute/pull/11369))
- **feat(video):** add an opt-in focused analysis mode that safely uses a normalized, 500-code-point latest-user hint for task-aware frame captions while preserving full-mode prompts, temporal-window isolation, and cache identity without storing raw task text ([#11383](https://github.com/diegosouzapw/OmniRoute/pull/11383)).
- feat(command-code): advertise low/medium/high/xhigh/max reasoning-effort suffixes for reasoning-capable models in the catalog and Combo Builder, with request-time resolution to reasoning_effort
- feat(crof): advertise reasoning-effort tiers (none/low/medium/high/max) for live-discovered and seed models, so the catalog, Playground, and Combo Builder surface <model>-<tier> aliases and requests resolve max upstream
- feat(sse): add Cursor plan image generation via Agent CLI (`IMAGE_PROVIDERS.cursor`, format `cursor-agent-image`), reusing the chat Cursor OAuth connection
- feat(routing): add the default-off `DISABLE_CONTEXT_WINDOW_CHECKS` feature flag to let operators bypass OmniRoute's local context-window and max-input-token check for direct single-model requests, leaving upstream limits, prompt compression, and output-token caps intact.
- **feat(catalog):** surface runtime-learned `reasoning_effort` tiers in `/v1/models` `capabilities.effort_tiers` (learned set replaces synced metadata when present), map them to OpenCode `ModelV2.variants` in the OmniRoute plugin, and align dispatch `-<tier>` suffix validation to the effective (learned ?? synced) set — so the UI offers exactly the tiers the upstream accepts (e.g. `{low, high, max}` for `oc/x-preview-f-free`) and each advertised variant completes. Excludes codex/glm/kimi, which keep their own dedicated `-{effort}` suffix mechanism and never gain `effort_tiers` from this path (related to #7694, builds on #11232)
- **feat(usage):** show Kimi Coding's fixed-order Code 5-hour/7-day quota windows plus Extra Usage status, balance, monthly spend/limit, and the official Additional Credits link on Dashboard → Quota cards.
- **feat(providers):** copilot-m365-web now supports OpenAI tool calling — a router planning turn asks the substrate model (as a tool-selection assistant emitting `CALL_TOOL: name({...})` / `NO_TOOL_NEEDED` text, which bypasses its plugin-registry refusal) and validated decisions surface as `tool_calls` with `finish_reason: "tool_calls"` in both stream and non-stream modes; also flattens the full message history (assistant `tool_calls` + compacted tool results) so multi-turn agent loops keep context, replies to SignalR `type:6` keepalives, surfaces `type:3` error frames instead of a silent empty `stop`, and suppresses `writeAtCursor` text from tool-progress frames
- **feat(api):** add `GET`/`POST` `/v1/multimodal-embeddings` as an alias of `/v1/embeddings` so Jina-compatible clients do not receive HTTP 404 `unknown_route` — thanks @RaviTharuma
- feat(opencode-go): expose Muse Spark 1.2 Contributor reasoning-effort aliases (minimal/low/medium/high/xhigh) in the Combo Builder
- **feat(providers):** restore the operator-owned upstream timeout tier per connection via `providerSpecificData.timeoutMs` (preempts the maintainer-only model/provider registry tiers and the global `FETCH_TIMEOUT_MS`), and make the combo per-target timeout ceiling follow the selected connection
- **feat(cli):** run `omniroute serve --tray` as a detached desktop process after server and tray readiness, with graphical login auto-start support.
- **feat(routing):** add client-, provider-, and model-neutral exclusive managed session connection leases with API-key-bound generation fencing, durable SQLite ownership, explicit allowlist policy, and bounded 429 capacity retry semantics.
## [3.8.54] — TBD
---
## [3.8.53] — 2026-11-06
_Living section — reconciled 2026-11-06 from all cycle commits (`c754707d0e` → `616801a2b6`, 1,162 non-merge commits). Bullets carry the merged PR and its author; direct pushes are listed with their commit hash. Regenerated at each `/generate-release` phase._
### 📊 Release by the numbers
| | |
| --- | ---: |
| 👥 People who contributed | **222** |
| 📝 Commits in the cycle | **1,162** |
| 🔀 Pull requests referenced | **847** |
| 📋 Changelog entries | **1,189** |
| 🙌 Contributors credited in entries | **221** |
| 🤖 Automated dependency commits | 11 |
**Entries by type**
| Type | Count |
| --- | ---: |
| 🐛 Fixes | 588 |
| ♻️ Refactor | 204 |
| ✨ Features | 146 |
| 🧪 Tests | 93 |
| 📚 Docs | 70 |
| 🧹 Chore | 51 |
| ⚙️ CI | 18 |
| ⚡ Performance | 10 |
| 📦 Dependencies | 7 |
| 🏗️ Build | 2 |
### 🏆 Top 25 contributors this cycle
_By commits in `c754707d0e..616801a2b6`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._
| # | Contributor | Commits |
| ---: | --- | ---: |
| 🥇 | diegosouzapw | 607 |
| 🥈 | Jan Leon (@JxnLexn) | 47 |
| 🥉 | Dizzle (@maxmad64bis) | 44 |
| 4 | Minxi Hou (@HouMinXi) | 40 |
| 5 | Paco Cartones (@pacocartones) | 39 |
| 6 | Ai Captain 357 (@AiCaptain357) | 27 |
| 7 | Andres Felipe Diago Matta (@AndresDiagoM) | 17 |
| 8 | Sean Ford (@seanford) | 16 |
| 9 | Fabio Junior Gruchovski (@fabioKoruja00) | 15 |
| 10 | Praveen K Palaniswamy (@yourspraveen) | 14 |
| 11 | 小妍儿 ✨ (@xiaoyaner0201) | 13 |
| 12 | Aldo (@aldoeliacim) | 11 |
| 13 | Junior-HJ | 9 |
| 14 | shin (@jjxyxsjr) | 8 |
| 15 | serenity (@serenity-X) | 8 |
| 16 | shevar0 | 8 |
| 17 | Bl0ck (@Bl0ck154) | 7 |
| 18 | opensource-elearning | 7 |
| 19 | Rafa Martins (@rafacpti23) | 7 |
| 20 | skygunner | 7 |
| 21 | trespassmk | 7 |
| 22 | Innokentiy Solntsev (@insoln) | 6 |
| 23 | Calvin Chen (@DarknessKiller) | 5 |
| 24 | Fouad Salkini (@fouadSalkini) | 5 |
| 25 | Kiaro-Sama (@KiaroSama) | 4 |
### ✨ New Features
- **feat(dashboard):** the dashboard and the standalone pages (login, status, error, legal, docs) run on the Keak CI UI Kit (KEA-7886), vendored as the `packages/ui-kit` workspace at a pinned upstream commit (`packages/ui-kit/UPSTREAM.md`, byte-identity guarded by `npm run check:ui-kit-snapshot`). The Keak identity replaces the old one — lime accent, self-hosted Inter / Instrument Serif / IBM Plex Mono, kit radius and surfaces, no background grid — and the theme follows the system by default with light and dark from the kit; the accent color presets in Settings › Appearance are retired (an old saved preset is ignored). The shell is the kit's sidebar and header with a Keak product switcher, and the command palette is a kit dialog. `@/shared/components` keeps its public API as adapters over the kit, so screens kept their imports, and every icon is a Lucide glyph through `<Icon>` / `ICON_MAP`. See `docs/architecture/DESIGN_SYSTEM.md`.
- **feat(teams):** add billing cost centers with temporal API-key assignments, shared soft budgets, and team usage reporting ([#10409](https://github.com/diegosouzapw/OmniRoute/pull/10409)) — thanks @hartmark / @xiaoyaner0201
- **feat(gemini):** add an opt-in `geminiPromptCacheMode` setting (`off` by default, `implicit`) scoped to Antigravity/AGY Gemini targets that preserves Gemini implicit prompt-cache evidence (`hit` / `miss` / `unreported` / `invalid`) across streaming, non-streaming, wrapped/unwrapped, OpenAI and Claude paths, never reports a cached count larger than the prompt, keeps prompt-cache affinity from reusing runtime-ineligible preferred accounts, and records bounded cache evidence in call-log metadata without leaking internal fields to clients (#12775, thanks @jbovard2016).
- **feat(release):** `npm run bun:release` (`scripts/release/bun-pack.mjs`) packs the already-built `dist/` into an npm-compatible tarball with `bun pm pack` (falling back to `npm pack`) for bun-first global installs; the Next.js build and prepublish step stay on Node. Thanks to @opensource-elearning (#12801).
- **feat(oauth):** GitHub Copilot connections record the account's plan, model-selection mode and quota state, and the dashboard shows a policy disclosure before the device-code login; Free / Student accounts use the catalog's `auto` model ([#12833](https://github.com/diegosouzapw/OmniRoute/pull/12833)) — thanks @ScottyAmr
- **feat(api):** Bifrost sidecar fast-path for `/v1/chat/completions` and `/v1/messages` with native fallback. The fast-path runs the API-key policy first, sends `/v1/messages` to the sidecar's Anthropic-compatible endpoint, never relays echoed credential or cookie headers, and falls back to the native pipeline on 5xx, network errors, or an unsupported endpoint ([#12953](https://github.com/cheaper-inference/OmniRoute/pull/12953) — thanks @rqzbeh)
- **feat(services):** sing-box embedded service — a supervised TPROXY sidecar installed from the official SagerNet release (pinned v1.14.1, SHA256-verified per platform; any other version or a checksum mismatch is refused). It gets the full embedded-service surface (8 loopback-only `/api/services/singbox/*` routes, dashboard tab, `SINGBOX_PORT`, seed migration 221). When TPROXY capture is applied, sing-box listens on its own port — never the native IP_TRANSPARENT listener's `onPort` — and every outbound is SO_MARK'd with the capture `bypassMark` the `mangle OUTPUT` rule excludes, so it neither collides with the native listener nor loops its own egress; without a `bypassMark` it is not started, and every failure path logs at error level. Thanks @rqzbeh. (#12962)
- **feat(routing):** add per-API-key ordered `preferredConnections`, allowing each project to pin a primary provider account with quota/cooldown-aware automatic fallback to the next eligible account without changing global connection priority. (#13102 - thanks @Bl0ck154)
- **feat(providers):** improve Antigravity and Cursor request handling and resilience ([#13125](https://github.com/diegosouzapw/OmniRoute/pull/13125)) — thanks @tuandinh0801
- **feat(providers):** Added ainetcafe as an OpenAI-compatible API-key provider (`https://microquickjs.com/v1`, Kimi K3 served from its own cluster), with live model discovery via `passthroughModels` ([#13862](https://github.com/diegosouzapw/OmniRoute/pull/13862)) — thanks @mario03690
- **feat(combo):** add an opt-in agentic-stall failover (`OMNIROUTE_AGENTIC_STALL_FAILOVER=1`, default OFF): a combo member that answers a tool-result turn with `finish: stop`, narration and no tool call now fails over to the next target, and its answer is returned only if no target does better ([#13954](https://github.com/cheaper-inference/OmniRoute/pull/13954) - thanks @patrykkopycinski)
- **feat(backend):** the proxy-registry bulk import ceiling is now an operator setting (`proxyBulkImportLimit`, default 100, hard cap 10,000) instead of a constant hardcoded in two places. It is written through `PATCH /api/settings` (`{"proxyBulkImportLimit": 500}`, integer 1–10,000). Previously `bulkImportProxiesSchema` and the dashboard's pre-flight check each carried their own literal `100`, so raising the limit meant editing source and the two could drift apart — a dashboard that accepted more rows than the API would. Both now resolve the same configured value through `resolveProxyBulkImportLimit`, the route reads it per request so a change applies without a restart, and the "maximum N proxies per import" message interpolates the real limit in all 66 locales rather than stating 100. Values above the cap clamp to the cap rather than falling back to the default, so an operator who asks for more than the ceiling gets the ceiling instead of silently less than they had. Fresh installs are unchanged ([#13917](https://github.com/diegosouzapw/OmniRoute/issues/13917)) ([#14074](https://github.com/cheaper-inference/OmniRoute/pull/14074)) — thanks @abhisheksharma2411
- **feat(cli-agents):** add WhyCodes as a first-class CLI Agent (`category: "agent"`, Chat Completions `/v1`) with catalog detection, `omniroute setup-whycodes` / `configure whycodes`, `omniroute run whycodes -P omniroute -m <id>`, and a dashboard apply path. `whycodes acp` is not implemented, so `acpSpawnable` stays false. ([#14096](https://github.com/diegosouzapw/OmniRoute/issues/14096)) ([#14107](https://github.com/cheaper-inference/OmniRoute/pull/14107)) — thanks @mrnasil
- **feat(providers):** add Kimi (Moonshot) as a web search provider via `POST /v1/tools/search`, joining the search gateway's failover ring ([#14292](https://github.com/diegosouzapw/OmniRoute/pull/14292)) — thanks @yourspraveen
- **feat(api):** `UNPRICED_USAGE_BUDGET_POLICY` feature flag chooses how per-key daily/weekly USD limits treat usage of a model with no configured price. `fail_closed` stays the default and keeps the #13257 verdict; `count_as_zero` counts that usage as $0 and still enforces priced spend. A key blocked only by unpriced usage now gets a 400 that names the unpriced models instead of a low "quota reached (N%)". Admins see the unpriced models on the dashboard home page, via a banner in every UI locale, and in `GET /api/pricing/unpriced-usage` ([#14608](https://github.com/diegosouzapw/OmniRoute/pull/14608), closes [#14604](https://github.com/diegosouzapw/OmniRoute/issues/14604)) — thanks @xiaoyaner0201
- **feat(api):** authenticate Claude Code (and any client) to `/v1/*` with a Microsoft Entra ID token instead of a static API key — Entra security groups map onto existing OmniRoute key groups (model allow/deny rules; per-key budgets and rate limits are unaffected by group membership) and usage is attributed per person. Ships a dependency-free sign-in helper (`scripts/cli/omniroute-sso.mjs`) for Claude Code's `apiKeyHelper`. Opt-in and additive: static API keys keep working ([#14665](https://github.com/diegosouzapw/OmniRoute/pull/14665)). — thanks @BenjaminAronsson
- **feat(usage):** display and claim Anthropic reset credits in the dashboard — parse banked usage grants (`cedar_ember`) and weekly 5-hour session resets (`juniper_tide`) from Claude OAuth usage, show available reset credits on Claude provider limit cards, and enable redemption via `/api/usage/codex-reset-credit` ([#14728](https://github.com/diegosouzapw/OmniRoute/pull/14728)). — thanks @fouadSalkini
- Enrich routing events with `apiKeyId` and the real streaming cost: OTel spans now carry `omniroute.api_key.id` and `gen_ai.usage.cost`/`.currency`, and the stream routing event is emitted once the cost lookup lands (null on failure). (#14736 — thanks @kenny000666 / @adevwithpurpose)
- **feat(providers):** dashboard control for the advertised Claude Code / Codex CLI client version, so operators can get past upstream model gates (Anthropic tiers, and OpenAI's "The 'gpt-6-astra' model requires a newer version of Codex") without an env var and a restart. One global override per provider kind; it sits above the env var and the automatic npm/GitHub version discovery, and the API/card surface the layer that actually won (`settings` / `env` / `discovered` / `default`), because "why isn't my override taking effect" is the usual support question and the answer is the precedence order. ([#14817](https://github.com/diegosouzapw/OmniRoute/pull/14817)) — thanks @adivekar-utexas
- **feat(dashboard):** assign selected models and combos to API keys from the catalog, with explicit confirmation before restricting an allow-all key. ([#14890](https://github.com/cheaper-inference/OmniRoute/pull/14890)) — thanks @fouadSalkini
- **feat(dashboard):** the combo test results modal gets a "Delete N failed model(s)" button that removes, after a confirmation, the combo's own steps whose every probe failed with a non-transient 4xx (timeouts, 408/429 and 5xx never count, and steps inside a nested combo are left alone); each result row now leads with the model id, with the account label and id underneath (#14944, thanks @skymanbj)
- **feat(providers):** the ChatGPT Web (Clean Room) adapter now supports client tool calls in bounded turns: `tools`, assistant `tool_calls` and `role: "tool"` continuations are accepted instead of rejected, emulated through the shared `<tool>{json}</tool>` envelope and returned as canonical `tool_calls` (JSON and SSE); each turn settles on the closing `</tool>`, is capped by `CHATGPT_WEB_TOOL_TURN_TIMEOUT_MS` / `CHATGPT_WEB_TURN_TIMEOUT_MS`, and browser/bridge failures no longer open the provider breaker or disable the connection; the browser now runs headless by default (`CHATGPT_WEB_HEADLESS=0` restores headed) ([#14948](https://github.com/diegosouzapw/OmniRoute/pull/14948)) — thanks @arashashrafii
- **feat(api):** opt-in quota session recovery: a non-streaming chat/responses turn that hits a terminal quota error can be parked (encrypted, 24 h) with `x-omniroute-quota-recovery: auto` and polled at `GET /api/v1/quota-recoveries/{id}` instead of failing ([#14982](https://github.com/diegosouzapw/OmniRoute/pull/14982), closes #14981) — thanks @dongwook-chan
- **feat(muse):** session-level two-account rotation with caller-bound reasoning ownership — fresh sessions round-robin across native OAuth accounts while continuations and tool-result turns stay pinned to owner connection, account identity, and credential generation (hashes only), failing closed on unknown owners or generation changes instead of silent cross-account replay; OAuth-narrowed (API-key operation preserved) with Muse semantic-cache bypass; empty Muse responses retry up to twice on the same owner without locking it out; a single OAuth account keeps working without a session ID, and sessions idle for 30 days are pruned so ownership rows stay bounded ([#15234](https://github.com/diegosouzapw/OmniRoute/pull/15234)) ([#15521](https://github.com/cheaper-inference/OmniRoute/pull/15521)) — thanks @kalpakprod / @mdc2122 / @SIGTERM-015 / @KSonny4 / @caniko / @Chewji9875
- **feat(oauth):** Route video generation, speech and transcription through the existing Grok Build OAuth connection, and image generation too when `GROK_SUBSCRIPTION_IMAGES_ENABLED` is on (same opt-in and request contract as the other Grok subscription image routes) ([#15305](https://github.com/diegosouzapw/OmniRoute/pull/15305)) — thanks @Kizuno18
- **feat(oauth):** Freebuff browser login on the existing `freebuff` provider card ([#15343](https://github.com/diegosouzapw/OmniRoute/pull/15343)) — mirrors `freebuff login` (fingerprint code → freebuff.com sign-in → status poll) and stores the resulting Codebuff auth token as an OAuth connection. Dual-auth keeps the pasted-token path. The token has no refresh grant, so it is marked non-refreshable and an expired token means signing in again. Closes #15336 — thanks @PranavD2905
- **feat(muse-code):** consolidate open Muse Code PRs — opencode-go sends `reasoning.effort=max` verbatim for muse-spark-1.3+ with one `xhigh` retry on the upstream unsupported-effort 400 ([#12687](https://github.com/diegosouzapw/OmniRoute/pull/12687)); `MuseCodeExecutor` restores the 24h `prompt_cache_retention` window the generic sanitizer strips ([#13634](https://github.com/diegosouzapw/OmniRoute/pull/13634)); device-flow hardening: origin-pinned verification URLs, 24h expiry cap, allowlisted poll errors with fixed messages, bounded auth requests ([#14267](https://github.com/diegosouzapw/OmniRoute/pull/14267)); mint-failure contract: 401/403 forces re-login, 429/transient keeps the stored key ([#14138](https://github.com/diegosouzapw/OmniRoute/pull/14138)) ([#15521](https://github.com/cheaper-inference/OmniRoute/pull/15521)) — thanks @Chewji9875 / @KSonny4 / @caniko / @SIGTERM-015 / @kalpakprod / @mdc2122
- **feat(oauth):** Import Antigravity Manager account exports with verified identities, bounded request bodies, account selection, and per-account results ([#15746](https://github.com/diegosouzapw/OmniRoute/pull/15746)). — thanks @mertcandrks
- **feat(resilience):** opt-in anti-avalanche failover guard (`OMNIROUTE_AVALANCHE_PROTECTION_ENABLED=true`, off by default) — when enabled, a 429 that names a tokens-per-minute limit no longer cascades over the provider's remaining keys and the model lockout uses a short cooldown instead of escalating, while a longer upstream reset always wins; quota-exhausted 429s keep failing over however large the prompt is and HTTP 413 keeps its retry-on-another-account behaviour ([#15775](https://github.com/cheaper-inference/OmniRoute/pull/15775) — thanks @ggdayup)
- **feat(combos):** the combo editor marks steps whose model is no longer in the provider's live catalog with a "Not in live catalog" badge, using the stale refs that `GET /api/combos/builder/options` now returns ([#15923](https://github.com/diegosouzapw/OmniRoute/pull/15923)) — thanks @tiagovilasboas
- Declare protection domains for existing CI job groups and reject missing or invalid declarations before dependency installation. Preserve current job scheduling and required checks while preparing the unified-policy transition. Refs #16075. ([#16142](https://github.com/cheaper-inference/OmniRoute/pull/16142))
- **feat(providers):** complete `claude-haiku-5-5` catalog wiring across Anthropic, Claude OAuth, Bedrock, and Vertex registries with length-tiered pricing ($0.10/$0.50 per MTok, $0.50/$2.50 above 100k prompt tokens) and Haiku 4.5 family fallback ([#16159](https://github.com/diegosouzapw/OmniRoute/pull/16159)) — thanks @tuandinh0801
- **feat(proxy):** accept selector-only subscriptions with no feed URL ([#16165](https://github.com/diegosouzapw/OmniRoute/pull/16165)) — thanks @maxmad64bis
- **feat(v1-models):** publish the routable `auto` and `auto/lkgp` ids in `GET /v1/models` with the pool limits ([#16224](https://github.com/diegosouzapw/OmniRoute/pull/16224)) — thanks @maxmad64bis
- **feat(api):** add read-only GET /v1/models/health reporting per-model pause state for the caller's key ([#16247](https://github.com/diegosouzapw/OmniRoute/pull/16247)) — thanks @maxmad64bis
- **feat(models-health):** report whether reasoning_effort none was honored per model on GET /v1/models/health ([#16253](https://github.com/diegosouzapw/OmniRoute/pull/16253)) — thanks @maxmad64bis
- **feat(dashboard):** the provider detail page now shows a badge on each model row listing the combos that use that model, built from a single `/api/combos` fetch ([#16307](https://github.com/diegosouzapw/OmniRoute/pull/16307) — thanks @farshadasadpour)
- **feat(providers):** add Token Harbor (`tokenharbor`) as an OpenAI-compatible API-key gateway with Bearer-key authentication, live model discovery from `/v1/models`, and a free-tier catalog entry for `claude-haiku-5.5:free` (#16315 - thanks @turbolego)
- Free-model catalog: add onomeo's 13 currently served free models (`auto`, DeepSeek V4 Flash, GLM 5.2/5.3/4.5/4.6v/4.7 Flash, Gemini 3.1 Flash Lite, GPT-OSS 20B, Muse Glimmer 30B, Codestral, Ministral 14B, HY4 Preview) as `recurring-uncapped` rows with no invented token figure (30 calls per 5 h in a shared pool), backed by per-model evidence records ([#16318](https://github.com/diegosouzapw/OmniRoute/issues/16318), thanks @opensource-elearning). ([#16325](https://github.com/cheaper-inference/OmniRoute/pull/16325))
- **feat(usage):** StepFun account balance is shown as a credits-style quota in Provider Limits, with the China (CNY) or international (USD) platform picked from the connection base URL ([#16348](https://github.com/cheaper-inference/OmniRoute/pull/16348) - thanks @Neuron-Mr-White)
- **feat(cli-tools):** the Pi setup flow now writes OmniRoute into Pi's current `~/.pi/agent/models.json` provider format (`PI_CODING_AGENT_DIR` honoured) instead of the legacy flat `~/.pi/config.json`, with a dedicated Pi card (endpoint, key, model, preview, apply, reset, backups), runtime/config-state detection, and ownership-marker safety so only the OmniRoute-owned provider is ever rewritten or removed; an unreadable or invalid `models.json` is refused with the shared 409 instead of being overwritten ([#16381](https://github.com/diegosouzapw/OmniRoute/pull/16381)) — thanks @arashashrafii
- **feat(proxy):** Operator-reported outages set a pool member aside briefly, with automatic return at expiry or on served traffic ([#16385](https://github.com/diegosouzapw/OmniRoute/pull/16385)) — thanks @maxmad64bis
- **feat(db):** a refused egress address is skipped on every pool member sharing it inside the same egress-bucketed provider scope ([#16421](https://github.com/diegosouzapw/OmniRoute/pull/16421)) — thanks @maxmad64bis
- **feat(core-config):** translate subscription URI and Clash nodes into outbounds instead of skipping them ([#16432](https://github.com/diegosouzapw/OmniRoute/pull/16432)) ([#16435](https://github.com/cheaper-inference/OmniRoute/pull/16435)) — thanks @maxmad64bis
- **feat(core-config):** render owned subscriptions to Mihomo YAML in addition to sing-box JSON ([#16435](https://github.com/diegosouzapw/OmniRoute/pull/16435)) — thanks @maxmad64bis
- **feat(core-config):** a subscription can now ingest several feeds with per-source isolation: a failing feed only skips its own nodes while the others still render ([#16439](https://github.com/diegosouzapw/OmniRoute/pull/16439)) — thanks @maxmad64bis
- **feat(proxy):** accept explicitly named host-first bulk proxy rows while preserving existing import formats, credential fields and safe validation errors ([#16450](https://github.com/diegosouzapw/OmniRoute/pull/16450)) — thanks @KiaroSama
- **feat(guardrails):** PII masking covers GDPR and POPIA identifiers (IBAN, EU VAT, Spanish DNI/NIE, Italian Codice Fiscale, Polish PESEL, Dutch BSN, German Steuer-ID, Swedish personnummer, South African ID/phone/VAT/tax reference). Every match must pass its checksum, structure or context validator before it is redacted on the request and response sides, so order numbers, timestamps and identifiers such as `gpt4o` are left alone. The ids join the per-type selection, settings schema and dashboard card, with new `gdpr` and `popia` templates; masking stays opt-in ([#16461](https://github.com/diegosouzapw/OmniRoute/pull/16461)) — thanks @zerotraceio
- **feat(dashboard):** no-auth provider cards now show a working inline toggle even when they have no connection rows, and the No Auth section gains Disable all / Enable all actions. Both write `settings.blockedProviders` through the alias-aware `computeNextBlockedProviders` helper (canonical id and alias stay in sync) and still forward the change to the existing connection toggle (#16553 — thanks @Junior-HJ).
- **feat(affinity):** optional Redis store (`OMNIROUTE_SESSION_AFFINITY_STORE=redis`) so multiple instances share session-to-account affinity by hashed account identity; default SQLite behaviour is unchanged ([#16569](https://github.com/diegosouzapw/OmniRoute/pull/16569)) — thanks @xiaoyaner0201
- **feat(combo):** Opt-in `quotaWeightedScoring: "pace"` for the `quota-weighted` strategy weights each account by its latest-resetting window's leftover per hour until that reset, so quota that is about to expire is spent first while the draw stays proportional and each account is weighted by its live plan's capacity (monthly price ratio); the default `"leftover"` scoring is unchanged ([#16572](https://github.com/diegosouzapw/OmniRoute/pull/16572), closes [#16458](https://github.com/diegosouzapw/OmniRoute/issues/16458)) — thanks @xiaoyaner0201
- Added opt-in passive Codex token refresh for replicas via `OMNIROUTE_TOKEN_REFRESH_PASSIVE_PROVIDERS=codex` (alias `cx`): reread synchronized tokens without rotation or terminal refresh failures, preserve normal upstream authentication cooldowns, store imports without a token exchange, and report a clear error for manual refresh. ([#16577](https://github.com/cheaper-inference/OmniRoute/pull/16577)) — thanks @xiaoyaner0201
- **feat(logs):** Export persisted call logs as OTLP/HTTP JSON traces, with optional Langfuse generation mapping, encrypted headers, per-destination pseudonymous identities, bounded requests and retries, and a create-time option to skip retained-history backfill. Optional `requestHeaderAllowlist` captures saved client request headers as standard OTLP string-array attributes (requires `includeBodies` and saved detailed pipeline logs; credential headers are always excluded). Export records now include nullable TTFT (new BigQuery tables include nullable `ttft_ms` INT64; existing tables keep exporting without it until the column is added). ([#16664](https://github.com/diegosouzapw/OmniRoute/pull/16664)) — thanks @xiaoyaner0201
- **feat(routing):** opt-in per-connection latency signal for p2c account selection (`resilienceSettings.p2cLatency.weight`, default off) ([#16737](https://github.com/diegosouzapw/OmniRoute/pull/16737)) — thanks @berthojoris
- **feat(resilience):** per-provider rate-limit retry delay — a "Retry delay" select on the provider page sets a fixed connection cooldown used when a rate-limit error carries no upstream retry hint ([#16741](https://github.com/diegosouzapw/OmniRoute/pull/16741)) — thanks @MuhammadUsamaMX
- **feat(api):** the API-only port (`API_PORT`) now also relays Gemini-native `/v1beta/*` routes, so Gemini CLI and google-genai SDK clients work against it ([#16745](https://github.com/diegosouzapw/OmniRoute/pull/16745)) — thanks @MuhammadUsamaMX
- **feat(api):** `GET /api/monitoring/model-health` reports a health state for every synced model on every provider connection (HEALTHY, DEGRADED, RATE_LIMITED, AUTH_FAILED, PROVIDER_DOWN, CONNECTION_BLOCKED, MODEL_LOCKED, UNKNOWN) from the connection state, the provider circuit breaker and the per-model lockout, with `provider`, `state` and `unhealthy=1` filters. Management-only, since the report carries connection ids and names. Auto-combo default weights are unchanged. (#16746 — thanks @Longvt1164)
- **feat(combos):** export/import combos as a standalone, secret-free JSON file — merge by default, explicit transactional replace, connections and credentials never travel ([#16747](https://github.com/diegosouzapw/OmniRoute/pull/16747)) — thanks @vianhanif
- **feat(dashboard):** per-step Test button in the combo create/edit modal — probes one model step (pinned account included) and shows latency or the error inline ([#16748](https://github.com/diegosouzapw/OmniRoute/pull/16748)) — thanks @smserwan
- **feat(dashboard):** hide/show models across all providers from the Models catalog — a Visible toggle, an All/Visible/Hidden filter and "Hide/Show filtered" bulk actions write the same per-provider visibility that `/v1/models` honors ([#16749](https://github.com/diegosouzapw/OmniRoute/pull/16749)) — thanks @MuhammadUsamaMX
- feat(providers): edit manual vision overrides on synced models and clear legacy vision overrides explicitly. (#16765) ([#16794](https://github.com/cheaper-inference/OmniRoute/pull/16794))
- **feat(plugin):** `@omniroute/opencode-plugin-v2` gains an opt-in `freeSignals` option so `freeOnly` can also admit models the pricing table prices at zero, ids ending in `-free`/`:free`, and `*-free` providers, labelled `(free, inferred)` ([#16800](https://github.com/diegosouzapw/OmniRoute/pull/16800)) — thanks @maxmad64bis
- **feat(cli-tools):** add OpenClaude setup guide card to the CLI Code catalog (OpenAI-compatible env vars + `openclaude --provider openai`) ([#16844](https://github.com/diegosouzapw/OmniRoute/pull/16844)) — thanks @ntdatt812
- **feat(api):** opt-in "expose combos only" setting so `/v1/models` advertises only combos (user combos + `auto/*`) while explicit provider/model ids keep routing ([#16846](https://github.com/diegosouzapw/OmniRoute/pull/16846)) — thanks @snower
- **feat(sse):** opt-in relevance-based tool disclosure (`settings.toolDisclosure`) keeps the client tools most relevant to the current turn (BM25) before the provider tool cap, never dropping tools already used, forced, pinned or named in the prompt; fails open on errors and offers a conservative `mode: "zeroOverlap"` that drops only tools sharing no word with recent messages ([#16861](https://github.com/diegosouzapw/OmniRoute/pull/16861)) — thanks @nitsuah / @vianhanif
- **feat(providers):** Amazon Bedrock embedding models are now reachable through `POST /v1/embeddings` — Titan Text V2/G1, Titan Multimodal G1, and Cohere Embed v4/English v3/Multilingual v3, including image embeddings, `dimensions` mapping, and OpenAI-shaped batching over Bedrock's per-family `InvokeModel` payloads ([#16869](https://github.com/diegosouzapw/OmniRoute/pull/16869)) — thanks @qwer2003tw
- **feat(dashboard):** Add an account-availability quota overview with per-pool capsule rows, cooldown recovery, account details, and browser-persisted provider reordering across layouts. ([#17026](https://github.com/diegosouzapw/OmniRoute/pull/17026) — thanks @xiaoyaner0201)
- **feat(sse):** opt-in `OMNIROUTE_EGRESS_QUOTA_ROTATION_PROVIDERS=all` sets a proxy-pool member aside after a `429` for every provider, not only the IP-bucketed opencode family, so free and keyless providers stop re-picking the member that just answered `429`; unset keeps today's behaviour byte-for-byte, and the clear-on-success arm and sibling-connection lockout stay narrow ([#17074](https://github.com/diegosouzapw/OmniRoute/pull/17074)) — thanks @opensource-elearning
- **feat(compression):** New opt-in flag `COMPRESSION_CACHE_AUTOMATIC_PREFIX_ESTIMATE` (default off): compression cache stats then count a request to an automatic-prefix-caching provider (OpenAI, Codex, Azure) as an estimated cache hit without `cache_control` markers. The estimate is a cache-eligibility signal, not an observed hit. With the flag off the stats are unchanged ([#17114](https://github.com/diegosouzapw/OmniRoute/pull/17114)) — thanks @aldoeliacim
- **feat(logs):** call-log rows record the client's own session id (`client_session_id`: opencode `x-opencode-session`, Claude Code `x-claude-code-session-id` / `metadata.user_id`, Codex `session_id`) next to OmniRoute's `session_tag`, so requests can be joined to the agent session that sent them ([#17125](https://github.com/diegosouzapw/OmniRoute/pull/17125)) — thanks @AndresDiagoM
- **feat(providers):** custom OpenAI chat-format compatible connections can set `providerSpecificData.toolNameMaxLength` to alias over-length tool names (e.g. a gateway in front of Bedrock Converse, 64 chars), and `anthropic-compatible-*` connections honor a declared `quotaEndpoint` for quota polling and quota-aware routing ([#17136](https://github.com/diegosouzapw/OmniRoute/pull/17136)) — thanks @guiguan
- **feat(dashboard):** the Custom Models editor can set a per-model Max Output Tokens override (blank clears it), wired to the existing `maxOutputTokenOverride` field of `PUT /api/provider-models` so `/v1/models` reports it for strict OpenAI-compatible clients ([#17140](https://github.com/diegosouzapw/OmniRoute/pull/17140)) — thanks @SaebAmini
- **feat(i18n):** the translation backend falls through a model chain — `OMNIROUTE_TRANSLATION_FALLBACK_MODELS` (comma-separated) backs the primary model in the docs wave and the UI-key translators: a request moves to the next model on 429 quota, 401/402, 5xx, timeout or network failure (never on a malformed request), each model keeping its own transient retry, with the per-chunk validator unchanged ([#17255](https://github.com/diegosouzapw/OmniRoute/pull/17255))
- **feat(api):** `GET /v1/models` entries carry an OpenRouter-compatible `pricing` object (`prompt`, `completion`, `input_cache_read`, `input_cache_write` as USD-per-token decimal strings). The existing per-1M numeric fields stay in the same object. Prices come from the pricing table cost accounting uses. Combos are now priced as their first visible target, synthetic variants copy their base model's price, free models read `"0"`, and an unknown price omits the object ([#17259](https://github.com/diegosouzapw/OmniRoute/pull/17259)) — thanks @fouadSalkini
- **feat(audio):** Add ElevenLabs sound effects, music composition, streaming, detailed responses and composition plans with key-scoped credentials, plus unified music generation and model discovery ([#17287](https://github.com/diegosouzapw/OmniRoute/pull/17287)) — thanks @felipemarques
- **feat(mcp):** `omniroute_perplexity_research` runs one Perplexity Deep Research report through the active perplexity-web connection named by `connectionId` (any other connection is refused). It is off by default and registered only when `OMNIROUTE_PPLX_RESEARCH_ENABLED=1`; each connection runs one report at a time with a one minute cooldown after a finished report, the remaining Deep Research quota is checked before a run, and the tool sends the same headers as the web executor (shared `buildPplxHeaders`) ([#17317](https://github.com/diegosouzapw/OmniRoute/pull/17317)) — thanks @HouMinXi
- `omniroute autostart enable` and `toggle` accept `--tray` / `--no-tray`, persist a successful explicit choice, and reuse it for future desktop launches. `serve` uses that preference unless explicitly overridden; systemd autostart remains without a tray. Tray hide guidance now recommends restarting with `--no-tray` instead of sending an unsupported signal. This is the CLI portion of #17330; dashboard controls and runtime tray toggling remain pending. ([#17413](https://github.com/cheaper-inference/OmniRoute/pull/17413))
- **feat(config):** refresh the OpenCode Zen free-model catalog to the 10 free ids the live `/zen/v1/models` endpoint serves today (8 added, 4 retired), with sourced evidence records for the new rows and the free-tier counts updated to 471 ([#17345](https://github.com/diegosouzapw/OmniRoute/pull/17345) — thanks @leoplife)
- **feat(discovery):** the provider page now shows the outcome of the last model discovery of each connection — a failed discovery keeps its cause and its age on the connection row instead of looking healthy on the next read ([#17346](https://github.com/diegosouzapw/OmniRoute/pull/17346)) — thanks @maxmad64bis / @marian-paun
- Show separately labeled post-first-token and end-to-end token rates in request logs, with a keyboard-accessible explanation of buffered delivery. Both use the same reasoning-aware token count; existing TPS sorting and missing-TTFT fallback keep their generation-rate semantics. (#17349) ([#17847](https://github.com/cheaper-inference/OmniRoute/pull/17847)) — thanks @morpheus9393
- **feat(search):** Exa 3.0 `type: "lite"` is billed at $0.004 per call (legacy `instant` too) and drops the fields lite rejects instead of failing with a 400; the deprecated `subpages`, `subpageTarget`, `highlights.numSentences` and `highlights.highlightsPerUrl` are no longer forwarded ([#17394](https://github.com/diegosouzapw/OmniRoute/pull/17394)) — thanks @yourspraveen
- **feat(search):** Exa search cost now follows Exa's billing: 10 results included, $0.001 per extra result, and $0.001 per result when `contents.summary` is set ([#17396](https://github.com/diegosouzapw/OmniRoute/pull/17396)) — thanks @yourspraveen
- **feat(chatgpt-web):** the clean-room provider reaches the GPT-6 family — `gpt-6` (Sol Instant), `gpt-6-thinking` (Sol Thinking, alias `gpt-6-sol`) and `gpt-6-pro` (Astra Pro) — alongside the existing GPT-5.6 and GPT-5.5 lanes, mapped onto the same first-party picker families (`GPT-6 Sol` / `GPT-5.6 Sol` / `GPT-5.5`) ([#17840](https://github.com/diegosouzapw/OmniRoute/pull/17840)) — thanks @skygunner
- **feat(sse):** key-based OpenCode Zen and Go requests follow the published endpoint, relay the client's own `x-opencode-*` headers and replay the tool parameter schemas the client already sent when `OPENCODE_PUBLISHED_CATALOG_ROUTING_ENABLED` is on, and keep the registry values unchanged while it is off ([#17864](https://github.com/diegosouzapw/OmniRoute/pull/17864)) — thanks @maxmad64bis
- **feat(search):** Tavily `search_depth: "advanced"` is recorded at 2 credits ($0.016) instead of 1 ([#17902](https://github.com/diegosouzapw/OmniRoute/pull/17902)) — thanks @yourspraveen
- **feat(search):** Linkup `depth: "deep"` is recorded at $0.05 per call instead of $0.005 ([#17903](https://github.com/diegosouzapw/OmniRoute/pull/17903)) — thanks @yourspraveen
- **feat(search):** Firecrawl search is recorded per started block of 10 results ($0.002 each), so 11 or more results are no longer under-recorded ([#17904](https://github.com/diegosouzapw/OmniRoute/pull/17904)) — thanks @yourspraveen
- **feat(providers):** CodeBuddy CN now imports its authenticated live model catalog while keeping a registry fallback ([#17916](https://github.com/cheaper-inference/OmniRoute/pull/17916)) — thanks @AStupidBear
- **feat(db):** Maintain SQLite query-planner statistics from the background cleanup scheduler (`PRAGMA optimize` with a bounded `analysis_limit`, never on a request path) so composite indexes are used with skip-scan instead of full scans; configurable via `OMNIROUTE_SQLITE_OPTIMIZE_ENABLED` and `OMNIROUTE_SQLITE_ANALYSIS_LIMIT` ([#17936](https://github.com/cheaper-inference/OmniRoute/pull/17936)) — thanks @xiaoyaner0201
- **feat(db):** Prune expired `semantic_cache` rows from SQLite in bounded background batches (yielding between batches, optional oldest-row cap, new `expires_at` index); previously expired rows were never deleted. Configurable via `OMNIROUTE_SEMANTIC_CACHE_PRUNE_INTERVAL_MS` (`0` disables), `OMNIROUTE_SEMANTIC_CACHE_PRUNE_BATCH_SIZE`, `OMNIROUTE_SEMANTIC_CACHE_PRUNE_MAX_BATCHES` and `OMNIROUTE_SEMANTIC_CACHE_MAX_ROWS` ([#17939](https://github.com/cheaper-inference/OmniRoute/pull/17939)) — thanks @xiaoyaner0201
- **feat(settings):** `outputLimitAdvisory` can be set once as a global setting instead of per combo — the combo resolver already read it from the settings row, but the key was missing from the settings schema and had no declared default, so the API stripped it and a fresh install handed the resolver `undefined`; the flag's semantics are unchanged ([#17958](https://github.com/diegosouzapw/OmniRoute/pull/17958)) — thanks @HouMinXi
- **feat(search):** Nimble search is recorded at the `lite` price it is sent at ($0.0011 per call) instead of $0.005 ([#17965](https://github.com/diegosouzapw/OmniRoute/pull/17965)) — thanks @yourspraveen
- **feat(api-keys):** add a per-key control for Claude Code discovery aliases so operators can keep compatibility aliases visible only where needed without widening the key's underlying model authorization ([#17979](https://github.com/diegosouzapw/OmniRoute/pull/17979)) - thanks @seanford
- **feat(i18n):** hardcoded English strings on the Conversations page, Log export, the CLIProxyAPI account health card, the Dario (Claude accounts) panel, the Telegram miniapp and eight other dashboard components now go through the message catalogs, with new keys translated for all 66 locales ([#17987](https://github.com/diegosouzapw/OmniRoute/pull/17987)) — thanks @HouMinXi
- **feat(api):** Added System One typed-decision models as a first-class model kind (`decisions`) with a public `POST /v1/systemone` route (#13987). The backend is selected by model id: `typesafe/jev-*` or a bare `jev-*` alias routes natively to api.typesafe.ai, `openrouter/<vendor>/<model>` routes to OpenRouter's alpha decisions endpoint using existing gateway credentials, quota preflight, cooldowns, call logs and `X-OmniRoute-*` cost telemetry. Decisions models never appear in chat pickers or combos. ([#17994](https://github.com/cheaper-inference/OmniRoute/pull/17994)) — thanks @Neuron-Mr-White
- **feat(api):** CLIProxyAPI management compatibility endpoints `GET /v0/management/auth-files`, `POST /v0/management/api-call` and `POST /v0/management/reset-quota`, so clients built for a CLIProxyAPI hub (such as T3 Code's `cliproxy` usage source) can read Claude/Codex subscription usage from OmniRoute with a `manage`-scoped key. `api-call` only allows the four Claude/Codex usage and reset-credit requests and injects the account token server-side. ([#17999](https://github.com/cheaper-inference/OmniRoute/pull/17999)) — thanks @rbansal42
- **feat(auth):** dashboard OIDC login can now be admitted by a role/group claim in addition to the subject/email allowlist. A new optional `oidcAdmissionClaim` setting (`{ path, values }`, written through `PATCH /api/settings`) is checked against the verified ID token before the dashboard session is minted; it fails closed on a missing, malformed or non-array claim, and when both the subject allowlist and the claim policy are set both must pass. The setting is Zod-validated, rejected when malformed, and counts as a security-impacting key (password re-auth required). A rejected login redirects to `/login?oidc_error=role_not_allowed`. Existing subject-only configurations are unchanged ([#17968](https://github.com/diegosouzapw/OmniRoute/issues/17968)) ([#18001](https://github.com/cheaper-inference/OmniRoute/pull/18001)) — thanks @insoln
- **feat(antigravity):** add `gemini_4_argon` (`argon`) quota family, weekly quota summary normalization (`gemini_4_argon_weekly` / `argon_weekly`), `detectAntigravityArgonEligibility` entitlement sync, and 404 retry short-circuiting for Gemini 4 Argon models ([#18026](https://github.com/cheaper-inference/OmniRoute/pull/18026)) — thanks @MrSiB
- **feat(audio):** `/v1/audio/transcriptions` accepts a multipart `url` as an alternative to `file` — OmniRoute downloads it through the SSRF-hardened remote media fetcher (public HTTPS only, DNS pinning, 2 redirects, 25 MiB and 15 s caps) and sends only the audio bytes to the provider, never the caller's URL (#18040). — thanks @Bl0ck154
- **feat(providers):** add Runcrate (`runcrate`), an OpenAI-compatible API-key provider (api.runcrate.ai) with six models ([#18061](https://github.com/diegosouzapw/OmniRoute/pull/18061)) — thanks @heydryft
- **feat(compression):** compression combos can select their own output styles (the unified output-style catalog with a level per style, previewed with the same builder the runtime uses); an empty selection inherits the Compression Engine panel, and a non-empty one supersedes the legacy output mode. A combo selected as the active profile now also applies its own output settings and language packs, not only its pipeline (#18097) ([#18107](https://github.com/cheaper-inference/OmniRoute/pull/18107)) — thanks @DarknessKiller
- **feat(providers):** add `MiniMax-M3.1-Flash` to the `minimax` and `minimax-cn` registries (after the default `MiniMax-M3`, 1M context, effort up to `max`) with pricing from MiniMax's pricing page, and update `MiniMax-M3` to its current base-tier price (0.30 / 1.20 / 0.06 per 1M tokens; the separate reasoning and cache-write rows are dropped, so MiniMax-M3 reasoning tokens now bill at the output price and cache writes at the input price) ([#18110](https://github.com/diegosouzapw/OmniRoute/pull/18110)) — thanks @octo-patch
- **feat(providers):** Add Gisoo (`gisoo`), a prepaid OpenAI-compatible multi-model relay, as an API-key provider with live model discovery from `/api/v1/models` ([#18112](https://github.com/cheaper-inference/OmniRoute/pull/18112)) — thanks @netarz / @heydryft
- **feat(opencode-plugin-v2):** publish one catalog branch with `prefix/*` and hide every combo with `combos: false` ([#18135](https://github.com/cheaper-inference/OmniRoute/pull/18135)) — thanks @maxmad64bis
- **feat(zcode):** an opt-in `ZCODE_TRANSPORT=gateway` streams native Anthropic responses from the Z.AI Individual Coding Plan straight to the client instead of buffering the whole turn through the ZCode app-server; it reads the plan's API key from the local ZCode profile (`~/.zcode/v2/config.json`, owner-only file, pinned `api.z.ai` origin) and is **experimental / unsupported**, since it reproduces the ZCode client protocol and may break when ZCode updates. The default `app-server` transport is unchanged and still falls back from the Individual Plan account profile to `builtin:zai-coding-plan`; `ZCODE_PROVIDER_ID` pins one exact profile ([#18190](https://github.com/cheaper-inference/OmniRoute/pull/18190) - thanks @megamen32)
- **feat(providers):** Command Code lists Kimi K3.1 Pro (`moonshotai/Kimi-K3.1-Pro`) in its static catalog with reasoning-effort variants, 1M context and a 384K output ceiling, so fresh or unsynced installs can route to it ([#18292](https://github.com/cheaper-inference/OmniRoute/pull/18292)) — thanks @berthojoris
- **feat(providers):** OpenCode Go serves Kimi K3.1 Pro (`kimi-k3.1-pro`): 1M context, 131K output, always-on thinking, with default pricing ([#18295](https://github.com/cheaper-inference/OmniRoute/pull/18295)) — thanks @cristianob
- **feat(compression):** RTK filter for PHPUnit test output — drops the banner, runtime header and dot progress, keeps failure headings, messages, diffs and the result summary ([#18317](https://github.com/cheaper-inference/OmniRoute/pull/18317)) — thanks @itseasy21
- **feat(providers):** Cloudflare Workers AI model discovery reads every page of the catalog and leaves embeddings, speech, classifier and guard models out of the chat list ([#18321](https://github.com/cheaper-inference/OmniRoute/pull/18321)) — thanks @askidmobile
- **feat(api):** `GET /api/models?withLimits=1` adds each model's resolved `contextWindow`, `maxInputTokens` and `maxOutputTokens` plus `capsSource`, naming the source each limit came from (persisted override, static authoritative table, models.dev sync, provider registry or static spec). The parameter is opt-in and resolved values are unchanged ([#18327](https://github.com/cheaper-inference/OmniRoute/pull/18327)) — thanks @mj10612
- **feat(dashboard):** the analytics Overview counters (tokens and estimated cost) animate toward their new value when the range or filters change; cost keeps its cents and `prefers-reduced-motion` is respected ([#18340](https://github.com/cheaper-inference/OmniRoute/pull/18340)) — thanks @F0Rextasy
- **feat(api):** `/v1/models` gives built-in and synced models a human-readable `name` (registry name first, then the synced name, else one derived from the id), and the Crush and OpenClaw config writers use the same label ([#18354](https://github.com/cheaper-inference/OmniRoute/pull/18354)) — thanks @asterxsk
- **feat(dashboard):** Persian, Arabic and Urdu now render in the self-hosted Vazirmatn font (`@fontsource/vazirmatn`, OFL-1.1). The font is named only under `html[dir="rtl"]`, so LTR locales never download it ([#18355](https://github.com/cheaper-inference/OmniRoute/pull/18355)) — thanks @m4tinbeigi-official
- **feat(db):** call log rows now persist `queue_wait_ms` — the wait from execution start to the first upstream dispatch ([#18402](https://github.com/cheaper-inference/OmniRoute/pull/18402)) — thanks @jjxyxsjr
- **feat(deploy):** the standalone launcher can keep a Render free web service awake by pinging its own public URL; inert unless `SELF_PING_URL` or `RENDER_EXTERNAL_URL` is set, with `SELF_PING_ENABLED=false` to opt out ([#18656](https://github.com/cheaper-inference/OmniRoute/pull/18656)) — thanks @Seizethewind
- **feat(providers):** extend `expiry-first` account rotation with requested quota-family scoping, plan-capacity weights, and observed reset-window burn metrics. Gemini and Claude windows no longer compete across families; Codex Spark uses its own quota windows. Accounts without burn history retain the existing quota/reset scoring. ([#16352](https://github.com/cheaper-inference/OmniRoute/pull/16352)) — thanks @Junior-HJ
- **feat(deploy):** first-class Kubernetes support — Kustomize base with k3s (Traefik + `local-path`) and generic k8s (ingress-nginx) overlays under `deploy/kubernetes/`, plus a Helm chart at `deploy/helm/omniroute/`. Both pin the single-SQLite-writer topology (`replicas: 1`, `Recreate`, ReadWriteOnce) and the chart refuses to render a configuration that would corrupt the database. Ingress defaults keep SSE unbuffered, and the probe defaults account for the synchronous SQLite driver: liveness is `tcpSocket` (an HTTP probe reads a checkpoint or VACUUM as a dead process and restarts the pod mid-write), with a 10-minute startup budget and a 300s termination grace period for the unbounded WAL checkpoint on shutdown. New `omniroute deploy k8s` prints the same manifests built from a running instance's settings, and `omniroute deploy k8s provision` prints the commands for standing up a k3s cluster locally or on a VPS — OmniRoute never runs them and never connects to the server. Guide: `docs/ops/KUBERNETES_DEPLOYMENT_GUIDE.md`. ([#12400](https://github.com/cheaper-inference/OmniRoute/pull/12400)) — thanks @PauloFH / @insoln
- **feat(dashboard):** <Icon> adapter with Material→Lucide map and fallback (KEA-7886, trilha B) (direct commit `f7cff7980a`)
- **feat(dashboard):** Keak lockup and accent ink on the 404 page (KEA-7886, trilha E) (direct commit `f6d522e69f`)
- **feat(dashboard):** DashboardLayout on SidebarProvider/SidebarInset with the legacy shell behind UI_KIT_SHELL (KEA-7886, trilha F) (direct commit `1f3d69de2a`)
- **feat(dashboard):** UI_KIT_SHELL runtime flag (kill switch for the kit shell) (KEA-7886) (direct commit `548608e860`)
- **feat(dashboard):** Keak lockup on login, error, status, maintenance and offline pages (KEA-7886, trilha E) (direct commit `6419eb1722`)
- **feat(dashboard):** Keak brand wrappers (ProductIcon/ProductLockup) and product switcher links (KEA-7886, trilha E) (direct commit `87c3d70669`)
- **feat(dashboard):** theme bridge sets data-theme for the kit and retires the accent presets (KEA-7886, D4) (direct commit `d8faae192e`)
- **feat(dashboard):** size dialogs to 70% of the viewport ([#14091](https://github.com/cheaper-inference/OmniRoute/pull/14091)) — thanks @zhiru
- **feat(opencode):** sanitize the OpenCode PR 4145 fingerprint ([#14142](https://github.com/cheaper-inference/OmniRoute/pull/14142)) — thanks @rafacpti23
- **feat(electron):** low-frequency health watchdog with auto-restart ([#17015](https://github.com/cheaper-inference/OmniRoute/pull/17015)) — thanks @for-the-zero
- **feat(i18n):** structure-safe Google Translate engine for the docs pipeline + ENVIRONMENT re-sync ([#18429](https://github.com/cheaper-inference/OmniRoute/pull/18429))
- **feat(i18n):** Google fallback and quota circuit breaker for every translation entry point ([#18461](https://github.com/cheaper-inference/OmniRoute/pull/18461))
- **feat(audio):** Gemini Live file-mode transcription — `gemini/gemini-3.5-transcribe-live` on `/v1/audio/transcriptions` converts the upload to 16 kHz PCM with a hardened `ffmpeg` (no stdin, pipe-only protocol whitelist, 25 MiB / 10 min caps, 60 s kill timeout) and transcribes it over the Gemini Live WebSocket, authenticating by header only ([#15776](https://github.com/cheaper-inference/OmniRoute/pull/15776) — thanks @ggdayup)
- **feat(auth):** opt-in TOTP two-factor authentication with single-use recovery codes for dashboard login; while it is on, `/api/cli/connect` also requires the code (`omniroute connect --totp`), and `OMNIROUTE_DISABLE_MFA=true` gives a host-only recovery path ([#16619](https://github.com/diegosouzapw/OmniRoute/pull/16619)) — thanks @tugrultamerc
- **feat(github):** Track GitHub Copilot AI Credits (`credits_used`) on the Provider Limits card and add an optional per-connection AI Credits limit that skips the account once the reported usage reaches it (fail-open on missing or stale readings; a local safeguard, not a guaranteed spending ceiling). ([#16688](https://github.com/diegosouzapw/OmniRoute/pull/16688)) — thanks @Primexz
- **feat(dashboard):** the home Provider Topology now shows live per-model sub-nodes with an On/Auto/Off toolbar (Auto, the default, draws only in-flight models) ([#16739](https://github.com/diegosouzapw/OmniRoute/pull/16739)) — thanks @maulinul
- **feat(command-code):** per-connection Zero Data Retention toggle for Command Code API keys — when on, every upstream call and the key-validation probe send `x-cmd-zdr: 1` ([#16751](https://github.com/diegosouzapw/OmniRoute/pull/16751)) — thanks @ZeroOttantotto
- **feat(usage):** TokenRouter quota tracker via an optional Management Key — wallet balance plus per-key quota on Provider Limits; the key is set in the connection modal ([#16837](https://github.com/diegosouzapw/OmniRoute/pull/16837)) — thanks @dasepmoch
- **feat(oauth):** Devin Desktop browser sign-in (Devin CLI PKCE login, fixed 127.0.0.1:59653 callback with paste-URL fallback) and live per-account model discovery via GetCliModelConfigs with a curated-catalog fallback ([#16843](https://github.com/diegosouzapw/OmniRoute/pull/16843)) — thanks @fadlee @kl3inIT
- **feat(audio):** support Gemini 3.8 TTS style metadata (`instructions`, `style`, `delivery`, `prompt`), pacing via `speed`, clean audio normalization (C2PA trailer strip and single WAV framing), and `response_format: "pcm"` ([#17265](https://github.com/diegosouzapw/OmniRoute/pull/17265)) — thanks @dkmscc
- **feat(sse):** add Kimi K3.1 Pro and Highspeed support across Moonshot and Kimi Coding, including 1M context/output, multimodal capabilities, and low/high/max reasoning-effort normalization ([#18038](https://github.com/cheaper-inference/OmniRoute/pull/18038)) — thanks @ysntony
- **feat(dashboard):** the home page gets two cards with host and OmniRoute process CPU and memory, served by the new management-tier `GET /api/system/metrics`; the cards poll every 3.5 s and pause while the tab is hidden ([#18393](https://github.com/cheaper-inference/OmniRoute/pull/18393)) — thanks @rafacpti23
- **feat(sse):** A combo can pin an OpenAI tier with `config.service_tier` (or `serviceTier`). It reaches `openai`, `azure-openai` and `codex` targets on every attempt, replacing the client's tier there, and other targets get the request as the client sent it. API writes reject values outside `auto`, `default`, `flex`, `priority` and `scale`. ([#18553](https://github.com/cheaper-inference/OmniRoute/pull/18553)) — thanks @dankarization
- **feat(docs):** npm and Docker installs now serve the translated `/docs` pages. The npm package (under `dist/docs/i18n/`) and the Docker image ship the 8,885 page mirrors the docs route can read, without the README/CHANGELOG/agent-guide mirrors or the operator-internal security pages, and `jsdom`, which renders them, is now a runtime dependency. The npm tarball grows from about 112 MB to 191 MB ([#18869](https://github.com/cheaper-inference/OmniRoute/pull/18869))
- **feat(dashboard):** the CI UI Kit's Editions palette (warm neutrals, copper accent) is now the default look in light and dark, across the dashboard, login, `/status`, error pages, `/docs` and the PWA; Settings → Appearance → Color palette keeps the previous lime look as Classic, and the README diagrams use the Editions palette ([#18901](https://github.com/cheaper-inference/OmniRoute/pull/18901))
- **feat(ci):** choose the docker-publish amd64 runner with DOCKER_AMD64_RUNNER ([#18858](https://github.com/cheaper-inference/OmniRoute/pull/18858))
- **feat(ci):** run docker-publish's prepare and merge jobs on Blacksmith too ([#18882](https://github.com/cheaper-inference/OmniRoute/pull/18882))
- **feat(ci):** fall back to hosted when the Blacksmith monthly minutes run out ([#18893](https://github.com/cheaper-inference/OmniRoute/pull/18893))
### 🐛 Bug Fixes
- **fix(authz):** with login disabled (`requireLogin=false`, the mode `omniroute setup` and the onboarding "skip" write), `/api/cli-tools/apply`, `/api/cli-tools/guide-settings`, `/api/cli-tools/hermes-agent-settings`, `/api/cli-tools/backups`, every `/api/log-export` route and `/api/restart` were reachable without authentication, unlike their already-protected siblings (`/api/cli-tools/codex-profiles`, `/api/logs/export`, `/api/shutdown`). They rewrite the operator's host CLI config, send call logs to a configured destination, or restart the server, so they now require a dashboard session or a manage-scoped API key in every mode. ([#18276](https://github.com/cheaper-inference/OmniRoute/pull/18276))
- **fix(providers):** validating an Auggie connection no longer starts the local `auggie` CLI for remote callers. The validator now honours the local-execution permission that remote-reachable routes (`POST /api/providers/validate`, bulk and import with key validation, connection tests) pass for non-loopback requests; those get "validation runs only for requests from this machine" instead. Local validation is unchanged. ([#18380](https://github.com/cheaper-inference/OmniRoute/pull/18380)) — thanks @AiCaptain357
- **fix(providers):** the ChatGPT Web browser providers no longer launch a caller-chosen executable. Validation of `chatgpt-web-codex` now honours the local-execution permission, so a remote management request (`POST /api/providers/bulk`, the connection test) never makes the host start a browser and gets "needs a request from this machine" instead; and a connection's stored `chromeExecutablePath` may only select a browser the operator configured (`CHROME_PATH`, `CHATGPT_WEB_CODEX_CHROME_PATH`, `CHATGPT_WEB_CHROME_PATH`) or a known Chrome/Chromium install location — any other path is ignored. If you pointed the dashboard at a Chrome in a non-standard location, set one of those variables instead. ([#18358](https://github.com/cheaper-inference/OmniRoute/pull/18358)) — thanks @AiCaptain357
- **fix(budget):** priced image (generations, upscale), speech, video, music and rerank calls now charge the per-key dollar budget and cost ledger. Those routes already priced every successful call for the `X-OmniRoute` cost header but never drew the budget down, so a key with a dollar budget could spend without limit on them. Only the metered share is charged, as on the chat path; flat-rate subscription providers stay exempt. ([#17404](https://github.com/cheaper-inference/OmniRoute/pull/17404))
- **fix(budget):** the per-key dollar budget (`/api/usage/budget`, the Budget tab) no longer lets a metered model with no pricing row through for free. Such a call used to be charged `$0` and dropped, so a budgeted key could keep spending on that model while the budget never moved. Like the per-key usage limit (#12341), the budget now fails closed for the rest of its period once a metered call it could not price completes (non-streaming and streaming), refusing further metered requests with `BUDGET_EXCEEDED`; `USAGE_LIMIT_IGNORE_UNPRICED=true` keeps counting that usage as `$0`, and saving the budget again reopens the period. Flat-rate subscription providers and provider-reported costs (xAI, OpenRouter) are unaffected. ([#17162](https://github.com/cheaper-inference/OmniRoute/pull/17162)) — thanks @0xLittleDream
- **fix(cli):** `omniroute launch`, `launch-codex`, `fcc-claude` and `run` no longer cmd-escape pass-through args when the resolved spawn has no shell — a native `claude.exe`/`codex.exe` (#9454) now receives argv untouched instead of literal caret-quoted text, which silently mangled every launch ([#17177](https://github.com/diegosouzapw/OmniRoute/issues/17177) ([#17178](https://github.com/cheaper-inference/OmniRoute/pull/17178)) — thanks @SammySN-car)
- Release the compression settings saved-status timer on unmount or a newer save, and ignore save completions after the panel has unmounted so they cannot schedule callbacks into a disposed UI. ([#17850](https://github.com/cheaper-inference/OmniRoute/pull/17850))
- **fix(authz):** harden the CLIENT_API CORS relax: the cross-origin Origin echo now follows a validated API key, and anonymous responses on installs with `REQUIRE_API_KEY=false` keep the fail-closed allowlist; a route-level 401 that refuses a presented key stays readable to browser and Electron clients (#0000) ([#18512](https://github.com/cheaper-inference/OmniRoute/pull/18512)) — thanks @HouMinXi
- **fix(models):** curated-only providers (Z.ai Web, Kimi Web, ChatGPT Web, CodeBuddy CN/intl) keep the custom models the operator added (#18018) but no longer list custom rows that an old model sync imported (`imported` / `api-sync` / `auto-sync`). Those providers have no upstream catalog, so such a row can only be a stale leftover of a retired discovery path — the obsolete Z.ai Web ids that 404 upstream (#8451) were coming back in the provider listing, `GET /api/providers/[id]/models` and `GET /v1/models`. ([#18516](https://github.com/cheaper-inference/OmniRoute/pull/18516))
- **fix(docker):** Patch npm's bundled Undici to 6.29.1 in Docker images and enforce the October security floor while preserving the compatible 6.x line. ([#18166](https://github.com/cheaper-inference/OmniRoute/pull/18166))
- **fix(sse):** the GHE Copilot token base URL now strips trailing slashes in linear time instead of with a regex that went quadratic on a long run of slashes in `gheUrl` (#0000) ([#18614](https://github.com/cheaper-inference/OmniRoute/pull/18614))
- **fix(deps):** raise the `@hono/node-server` override to `^2.1.3` (resolves 2.1.5), which fixes the double path decoding in `serveStatic`; OmniRoute uses the package only through the MCP SDK's Streamable HTTP transport (#0000) ([#18615](https://github.com/cheaper-inference/OmniRoute/pull/18615))
- **fix(sse):** Claude Code's internal security-classifier turns no longer read from or write to Memory, so a classifier's output format cannot become a stored fact that is replayed into later requests (#0000) ([#18622](https://github.com/cheaper-inference/OmniRoute/pull/18622))
- **fix(quota):** the per-key monthly USD quota (`/api/usage/key-quota`) no longer lets a metered model with no pricing row through for free. Such a call never reached the cost ledger the cap sums, so a capped key could keep spending on that model all month. Like the dollar budget and the per-key usage limit (#12341), the monthly quota now fails closed for the rest of the calendar month once a metered call it could not price completes (non-streaming and streaming), refusing further requests with `429`; `USAGE_LIMIT_IGNORE_UNPRICED=true` keeps counting that usage as `$0`, and saving the quota again reopens the month. Keys without a monthly cap are unaffected. ([#17402](https://github.com/cheaper-inference/OmniRoute/pull/17402))
- **fix(providers):** validating a Qoder Personal Access Token (`pt-…`) no longer starts the local `qodercli` for remote callers. The validator now honours the local-execution permission that remote-reachable routes (`POST /api/providers/validate`, import and bulk with key validation — including through the `if` alias —, connection tests) pass for non-loopback requests; those get "validated with the local qodercli, which runs only for requests from this machine". Regular Qoder API keys still validate over HTTP, and local validation is unchanged. ([#18424](https://github.com/cheaper-inference/OmniRoute/pull/18424)) — thanks @AiCaptain357
- Prepare pinned, checksum-verified actionlint and zizmor binaries before both Release-Green observer lanes; fail setup on invalid downloads or versions and use the immutable workflow revision when auditing older target commits. ([#17846](https://github.com/cheaper-inference/OmniRoute/pull/17846))
- Bind hosted Release-Green suite receipts to their exact commit, workflow, run/attempt, Node patch, lockfile, command/shard and retained log hash; reject stale, malformed, unexpected or duplicate evidence. Artifacts are attempt-scoped, so partial reruns require rerunning all suite jobs before a complete verdict. Pass suite context explicitly through the CI command rather than product environment settings, and fail the hosted job if its receipt cannot be written. ([#17851](https://github.com/cheaper-inference/OmniRoute/pull/17851))
- Release-Green rejects incomplete workflow audits instead of accepting missing or broken scanners as advisory drift. Its report verifier requires explicit measured evidence for workflow checks, and scheduled-run/provenance policy violations remain blocking while measured ratchet debt keeps its existing classification. ([#17412](https://github.com/cheaper-inference/OmniRoute/pull/17412))
- Restore the strict core TypeScript check by typing Responses completion state, dispatcher cache keys, and database settings updates without changing their runtime behavior. ([#17857](https://github.com/cheaper-inference/OmniRoute/pull/17857))
- **fix(pwa):** the service worker no longer intercepts or caches the client-API aliases (`/v1`, `/v1beta`, `/models`, `/chat`, `/responses`, `/codex`) or `/callback` and `/login`, and matches its bypass list case-insensitively like the server router (#0000) ([#18512](https://github.com/cheaper-inference/OmniRoute/pull/18512)) — thanks @HouMinXi
- **fix(api):** the Telegram bot (`/api/telegram/update`, webhook and Mini App) can now be limited to specific Telegram users: set `TELEGRAM_USER_ALLOWLIST_ENABLED=true` and list numeric user ids in `TELEGRAM_ALLOWED_USER_IDS` (invalid entries and an unrecognised switch value are reported once at startup); unlisted senders get 403 on the Mini App and their webhook messages are dropped. Off by default, so existing bots keep serving every user (#0000) ([#18475](https://github.com/cheaper-inference/OmniRoute/pull/18475)) — thanks @HouMinXi
- **fix(sse):** the Codex rejected-token fingerprint, the Muse session-ownership digest, the Z Code per-key state map and the Entra token cache key now go through `opaqueTokenFingerprint()` instead of an inline `createHash`; outputs are byte-identical, so stored values keep matching (#0000) ([#18613](https://github.com/cheaper-inference/OmniRoute/pull/18613))
- **fix(api):** Apply `REQUIRE_API_KEY` to the Responses WebSocket handshake and refuse cross-site browser `Origin`s on the WebSocket transports that return completions (`/v1/responses`, and the `/v1/ws` bridge used by `npm start` / `npm run dev`), so a web page can no longer open them on a keyless install and read completions. Clients that send no `Origin` (Codex CLI, SDKs), same-origin pages, allowlisted origins and valid API keys keep working. ([#18512](https://github.com/cheaper-inference/OmniRoute/pull/18512)) — thanks @HouMinXi
- **fix(antigravity):** skip the `x-goog-user-project` header when the request body carries the `aicode-consumers` default project, the same way the `test-project` / `project-id` placeholders already are ([#12499](https://github.com/diegosouzapw/OmniRoute/pull/12499)) — thanks @Abhishekchhetri020
- **fix(providers):** AI/ML API model discovery works again: the live `/models` catalog is read from its `{ object, data }` envelope and the current `openai/chat-completions` type (non-chat rows never reach the picker), the static seed swaps four retired ids for six verified ones, and optional params sent as literal `null` (which AI/ML API rejects with 400) are dropped before dispatch ([#13668](https://github.com/cheaper-inference/OmniRoute/pull/13668)) ([#18161](https://github.com/cheaper-inference/OmniRoute/pull/18161)) — thanks @hugoaimlapi / @Lookoff-AIMLAPI
- **fix(auto):** constrain auto-combo candidate expansion, scoring, and fallback ordering to each API key's allowed provider connections, failing closed when the permitted active account set cannot be established. ([#17981](https://github.com/cheaper-inference/OmniRoute/pull/17981)) — thanks @seanford
- Bound auto-candidate inspection work by projecting only routing identities, bulk-loading reachability metadata, memoizing provider breaker reads, and yielding cooperatively while large pools are built. ([#17986](https://github.com/cheaper-inference/OmniRoute/pull/17986)) — thanks @seanford
- Harden embedded Bifrost by keeping its cache under the data directory, bypassing provider proxying for loopback health checks, and exposing only the spawn-free cliproxy status route remotely. ([#17976](https://github.com/cheaper-inference/OmniRoute/pull/17976)) — thanks @seanford
- Reconcile stale embedded-service lifecycle state after a process restart when no in-memory supervisor exists. ([#17977](https://github.com/cheaper-inference/OmniRoute/pull/17977)) — thanks @seanford
- **fix(mcp):** bound and paginate `omniroute_list_models_catalog` results with stable cursors, search, summary mode, explicit totals, and per-provider failure reporting so large or partially unavailable catalogs remain usable. ([#17973](https://github.com/cheaper-inference/OmniRoute/pull/17973)) — thanks @seanford
- Let image and speech combos continue past target-local 400, 401, and 403 failures only when a later target uses a distinct provider or explicitly different account, while preserving API-key connection scoping. ([#17989](https://github.com/cheaper-inference/OmniRoute/pull/17989)) — thanks @seanford
- Preserve safe structured model-discovery failure diagnostics without exposing upstream body text, credentials, or malformed headers, while keeping intentional no-auth catalogs syncable. ([#17984](https://github.com/cheaper-inference/OmniRoute/pull/17984)) — thanks @seanford
- **fix(health):** probe only local provider nodes backed by an active connection or a visible configured model, avoiding repeated health traffic to stale node definitions while preserving recovery checks for routable providers. ([#17983](https://github.com/cheaper-inference/OmniRoute/pull/17983)) — thanks @seanford
- Attribute embeddings and rerank call logs to the originally requested model and combo across direct calls, combo targets, fallbacks, successes, and upstream failures. ([#17988](https://github.com/cheaper-inference/OmniRoute/pull/17988)) — thanks @seanford
- **fix(muse):** floor tiny caller output budgets for `muse-code` (e.g. health probes sending `max_output_tokens: 16`) to `MUSE_SPARK_MIN_OUTPUT_TOKENS = 512`. Muse spends the budget on hidden reasoning, so these requests ended `response.incomplete` + `response.failed` "Provider returned empty content", which OmniRoute turned into a `upstream_empty_response` 502 plus a model lockout ([#15234](https://github.com/diegosouzapw/OmniRoute/pull/15234)) ([#15521](https://github.com/cheaper-inference/OmniRoute/pull/15521)) — thanks @kalpakprod / @mdc2122 / @SIGTERM-015 / @KSonny4 / @caniko / @Chewji9875
- Prevent replay of Grok/xAI video creates on ambiguous transport failures, propagate client cancellation through video combos and job polling, and enforce deadlines, response-size limits and caller poll caps without resubmitting jobs. ([#15305](https://github.com/cheaper-inference/OmniRoute/pull/15305)) — thanks @Kizuno18
- **fix(providers):** Cline / ClinePass / Kilo Code dual-auth — BYOK API keys are no longer sent as `Bearer workos:<key>` and the key-validation probe no longer fails on a paid model or a duplicated URL path: the `Authorization` shape is now decided by the credential shape (`accessToken` ⇒ WorkOS-prefixed OAuth token, `apiKey` ⇒ plain bearer key) instead of by `apiKey || accessToken` order, the Cline and Kilo Code validators probe the free `openrouter/free` model (the registry's first model, `z-ai/glm-5.2`, is paid and answered `402` for accounts without credit, which the probe reported as an invalid key), and both validators now resolve the chat endpoint through the executor's own `normalizeOpenAIChatUrl()` so a custom base URL no longer produced the unserved `/api/api/v1/chat/completions` route ([#16298](https://github.com/diegosouzapw/OmniRoute/pull/16298)) — thanks @raheemuddin786
- **fix(translator):** Claude Code `WebSearch` routed to Codex now uses Codex's native `web_search` instead of the empty `omniroute_web_search` fallback and returns `server_tool_use` + `web_search_tool_result` blocks ([#16365](https://github.com/diegosouzapw/OmniRoute/pull/16365)) — thanks @wray-lee
- Publish the optional runtime packs generated by Desktop builds alongside the installers. Each app's bundled index selects its exact archive by SHA-256, preventing different platform builds from overwriting one another. Local pack installation now honors the indexed archive name and verifies its archive checksum before extraction, while preserving legacy indexes and per-package integrity checks. Refs #16538. ([#17338](https://github.com/cheaper-inference/OmniRoute/pull/17338))
- fix(sse): preserve explicit OpenAI, Responses, Claude and recognized thinking-level effort in adaptive mode while retaining automatic budget scaling and provider capability sanitization (refs #16662). ([#17333](https://github.com/cheaper-inference/OmniRoute/pull/17333))
- **fix(sse):** distinguish explicit OpenCode free-tier restrictions from credential failures in combo error messages, without changing upstream status or routing; this diagnostic correction does not restore access to restricted free-tier models ([#16795](https://github.com/diegosouzapw/OmniRoute/pull/16795)) — thanks @KiaroSama
- fix(sse): preserve provider-native model IDs such as Ollama Cloud `gpt-oss:120b` in explicit routes and local aliases while retaining foreign-dialect compatibility (refs #16823). ([#17336](https://github.com/cheaper-inference/OmniRoute/pull/17336))
- fix(gemini-web): keep the active browser leased, share one pending launch, classify closed-browser errors as 502/499/504 and make the response wait configurable (#16935) ([#18195](https://github.com/cheaper-inference/OmniRoute/pull/18195))
- **fix(veoaifree):** executor forwards stored web-session credentials in Cookie header and fails fast with 401/429/502 on unauthenticated HTML instead of polling for ten minutes ([#17047](https://github.com/diegosouzapw/OmniRoute/pull/17047)) — thanks @PrinceThummar011
- fix(codebuddy): harden CodeBuddy gateways (CN + Intl) with tool schema normalization, turn order alignment, and non-enumerable tool-name alias tracking (#17080) — thanks @Junior-HJ
- **fix(sse):** tool parameters with a root anyOf union are merged into one object schema on Kimi and Moonshot instead of being reduced to an empty schema; only arguments required by every branch stay required, and a property declared by several branches keeps its first definition ([#17105](https://github.com/diegosouzapw/OmniRoute/pull/17105)) — thanks @maxmad64bis
- **fix(oauth):** serializes Amazon Q and Openference token refreshes in their rotation lane so sibling accounts no longer POST rotating refresh tokens concurrently; the quota path and the connection test now treat both as rotating providers (no auto-refresh of an expired token during a connection test) ([#17106](https://github.com/diegosouzapw/OmniRoute/pull/17106)) — thanks @maxmad64bis
- **fix(resilience):** Recover Claude OAuth connections after observed weekly-quota resets only when a newer same-connection reset event and positive quota telemetry confirm recovery. Preserve monthly-spend, RPM and terminal restrictions, unresolved model-specific locks and concurrent cooldowns; keep renewing Claude quota failures nonterminal ([#17107](https://github.com/diegosouzapw/OmniRoute/pull/17107)) — thanks @fabioKoruja00; fixes #17043.
- **fix(claude):** Preserve Haiku's capped output allowance when fitting a thinking budget so requests with large `max_tokens` and low reasoning effort retain room for the response ([#17110](https://github.com/diegosouzapw/OmniRoute/pull/17110)). — thanks @DavyMassoneto
- **fix(compression):** Preserve every current-turn message across exact, intra-message, and fuzzy session deduplication while continuing to compress completed history. ([#17115](https://github.com/cheaper-inference/OmniRoute/pull/17115)) — thanks @aldoeliacim
- **fix(sse):** a second 429 with a shorter cooldown no longer shortens the active cooldown on a combo target — the longest cooldown now wins ([#17116](https://github.com/diegosouzapw/OmniRoute/pull/17116)) — thanks @maxmad64bis
- **fix(sse):** `/v1/messages` responses from a non-native-`claude` provider (Antigravity, Codex, Gemini, OpenAI-compatible) now restore each tool-call name to the spelling the client declared instead of upgrading known Claude Code tools to PascalCase, so third-party Anthropic-format clients that declare lowercase tools (OpenCode's `@ai-sdk/anthropic` provider) stop receiving `Read` for a tool they declared as `read`; Claude Code, which declares `Read`, keeps getting `Read` back ([#17119](https://github.com/diegosouzapw/OmniRoute/pull/17119)) — thanks @AndresDiagoM
- Carry the client tool declarations through the Claude SSE passthrough caller as well, preserving explicit empty declarations and the legacy body.tools fallback. ([#17119](https://github.com/cheaper-inference/OmniRoute/pull/17119)) — thanks @AndresDiagoM
- **fix(sse):** a priority combo whose kept targets are all skipped before a single dispatch (cooldown, open breaker, exhausted) is now served by a compat-rejected-but-healthy target instead of a 503, completing the round-robin last-resort compat fallback on the universal dispatch path ([#17120](https://github.com/diegosouzapw/OmniRoute/pull/17120)) — thanks @maxmad64bis / @ThongAccount
- **fix(sse):** ping-only upstreams now fail at the first readiness deadline instead of the ceiling, a stream that only sends `retry:` or `id:` frames no longer keeps the readiness deadline alive, and non-JSON garbage under `data:` is refused at the readiness gate instead of being replayed as a false 200 ([#17123](https://github.com/diegosouzapw/OmniRoute/pull/17123)) — thanks @maxmad64bis
- **fix(xai):** Codex MCP `namespace` tools are flattened before xAI `/v1/responses` (no more 422 `unknown variant namespace`), and 422 body-shape errors no longer cool down the account ([#17134](https://github.com/diegosouzapw/OmniRoute/pull/17134)) — thanks @yaman0786
- **fix(sse):** Uppercase or mixed-case spellings of a shut-down model id are now rejected with the 410 that cites the canonical record id, instead of being routed. ([#17135](https://github.com/diegosouzapw/OmniRoute/pull/17135)) — thanks @maxmad64bis
- **fix(validation):** OpenAI-, Anthropic- and Claude Code-compatible connection tests send the connection's `customHeaders`, so a gateway behind an access proxy is no longer reported as "Invalid API key" and skipped by the credential gate ([#17136](https://github.com/diegosouzapw/OmniRoute/pull/17136)) — thanks @guiguan
- **fix(responses):** preserve tool calls when reasoning-tagged models emit `<think>` text and tool metadata in the same streamed chunk ([#17144](https://github.com/diegosouzapw/OmniRoute/pull/17144)) — thanks @Vaishnavi220506
- **fix(syntx):** SYNTX media fetched by URL and the data-URL reference decodes are now capped at 50 MiB, so an oversized or hostile media answer fails fast with a bounded 413/400/499 instead of growing unbounded in memory ([#17152](https://github.com/diegosouzapw/OmniRoute/pull/17152)) — thanks @maxmad64bis
- **fix(cache):** Version exact-cache identities and canonicalize response controls so changed token caps, reasoning settings, or tool policy cannot reuse an incompatible response ([#17161](https://github.com/diegosouzapw/OmniRoute/pull/17161)). — thanks @mikeng-io
- **fix(cursor):** Preserve structured model-entitlement refusal titles and guidance across Connect and JSON error paths, and keep explicit weekly-usage model restrictions scoped to the selected connection/model instead of treating them as account-wide failures ([#17182](https://github.com/diegosouzapw/OmniRoute/pull/17182)) — thanks @xiaoyaner0201
- Contain two malformed-upstream payloads as request-scoped failures instead of provider outages: a Responses stream that aborts with a bare error frame (and never settles on a terminal or completed snapshot) now reports `status: "failed"` with that error rather than collapsing to an empty success, and non-object or unparseable tool-call arguments now raise a typed `SyntaxError` instead of escaping the non-streaming translator. Empty, missing or `null` tool-call arguments still translate to an empty `input: {}`. ([#17198](https://github.com/cheaper-inference/OmniRoute/pull/17198)) — thanks @aldoeliacim
- **fix(sse):** stop injecting `outputDimensionality` into Gemini embedding requests sent to the OpenAI-compatible endpoint — sized requests failed with a 400 "Unknown name outputDimensionality"; `dimensions` is now forwarded untouched, the combo-level override still works and the native `embedContent` path keeps mapping it through `geminiRequestExtras` ([#17208](https://github.com/diegosouzapw/OmniRoute/pull/17208), fixes [#17158](https://github.com/diegosouzapw/OmniRoute/issues/17158)) — thanks @rafacpti23 / @PrinceThummar011
- **fix(executors):** Preserve caller-supplied `reasoning.context` on Codex HTTP and WebSocket Responses requests while continuing to remove unsupported OpenRouter reasoning keys. App-server and chatgpt-web-codex transports are unchanged. (#17210 — thanks @seathatflowsinourveins)
- **fix(batch):** Reject array-valued Anthropic `params` during JSONL validation and exclude invalid lines from the preview ([#17211](https://github.com/diegosouzapw/OmniRoute/pull/17211)) — thanks @pacocartones
- **fix(electron):** resync `electron/package-lock.json` with its `package.json` — the v3.8.52 tag's lockfile lacked the `electron-builder-squirrel-windows`, `electron-winstaller`, `@electron/windows-sign` and `postject` subtrees, so `npm ci` failed the Windows and Linux desktop builds of the release; regenerated with `--package-lock-only` (195 lines added, 0 removed, no version changes) and the desktop assets were rebuilt from the repaired line ([#17212](https://github.com/diegosouzapw/OmniRoute/pull/17212))
- **fix(batch):** Upload complete JSONL files instead of preview samples and read every CSV row before batch conversion ([#17213](https://github.com/diegosouzapw/OmniRoute/pull/17213)) — thanks @pacocartones
- Preserve the per-key quota-share error code through JSON responses so combos do not lock a shared connection's model when one API key reaches its allocation. Keep the quota reason and available retry hint in the client response ([#17214](https://github.com/diegosouzapw/OmniRoute/pull/17214)) — thanks @rafacpti23. Fixes #17132.
- Preserve distinct Bedrock tool calls when a normalized history ID collides with an existing valid ID, normalize Claude tool results with the same request mapping, and omit empty-ID pairs consistently with OpenAI history. Fixes #17143. ([#17217](https://github.com/cheaper-inference/OmniRoute/pull/17217)) — thanks @rafacpti23
- **fix(cli):** `omniroute restart` now returns to the shell — it stopped the daemon and then started the new server in the foreground supervisor (no `daemon: true`), so it never exited and blocked scripts and scheduled tasks; the restarted server now detaches like `serve --daemon` ([#17218](https://github.com/diegosouzapw/OmniRoute/pull/17218)) — thanks @yourspraveen
- **fix(batch):** Discard pending file contents after the input format changes, preserving the new selection and allowing subsequent reads ([#17221](https://github.com/diegosouzapw/OmniRoute/pull/17221)) — thanks @pacocartones
- **fix(batch):** Preserve original CSV columns while remapping, invalidate outdated conversions, and retain mapping readiness when returning to the input step ([#17222](https://github.com/diegosouzapw/OmniRoute/pull/17222)) — thanks @pacocartones
- **fix(streaming):** Register stream failure finalizers before starting the eager response pipeline so Perplexity errors after partial content retain failure callbacks and accounting ([#17226](https://github.com/diegosouzapw/OmniRoute/pull/17226)) — thanks @pacocartones
- **perf(provider-stats):** provider and model call statistics now cover the last 7 days instead of the full history (the window start is shown, and `?days=` changes the lookback), keyless providers keep their stats while providers whose connections are gone drop out, and the 30-second dashboard refresh pauses while the tab is hidden; the payload is served through the shared analytics response cache ([#17227](https://github.com/diegosouzapw/OmniRoute/pull/17227)) — thanks @maxmad64bis
- **fix(sse):** the process crash guard now treats an account-semaphore capacity rejection (`SEMAPHORE_TIMEOUT` / `SEMAPHORE_QUEUE_FULL`) that reaches `unhandledRejection` or `uncaughtException` as a handled capacity signal instead of exiting the gateway under load ([#17229](https://github.com/diegosouzapw/OmniRoute/pull/17229)) — thanks @minhlongs
- **fix(api):** Honor existing exact and wildcard model aliases in multipart audio transcriptions while preserving combo precedence and resolved-target API-key policy ([#17232](https://github.com/diegosouzapw/OmniRoute/pull/17232)) — thanks @edosulai
- **fix(cli):** Preserve Windows CLI executable discovery and `where.exe` probing in Linux-built server bundles by reading the runtime OS instead of a build-time-foldable platform constant ([#17233](https://github.com/diegosouzapw/OmniRoute/pull/17233)) ([#17234](https://github.com/cheaper-inference/OmniRoute/pull/17234)) — thanks @fabioKoruja00 / @PrinceThummar011
- **fix(cli):** preserve Windows global npm prefix and system Node directory resolution in Linux-built bundles ([#17234](https://github.com/diegosouzapw/OmniRoute/pull/17234)) — thanks @PrinceThummar011 / @fabioKoruja00
- **fix(mcp,a2a):** Honor explicit combo selection in MCP `route_request` and A2A `smart-routing`, and distinguish requested combos from gateway-confirmed routing in A2A decision telemetry ([#17235](https://github.com/diegosouzapw/OmniRoute/pull/17235)) — thanks @fabioKoruja00
- **fix(docker):** the Bun `runner-web` image (`omniroute:<version>-web-bun`) builds again — its `apt-get install` asked for `gconf-service` (gone from Debian 12+), `stdc++6` (typo for `libstdc++6`), `pangocairo-1.0-0` (`libpangocairo-1.0-0`) and `fonts-kacst` (no candidate on the Bun slim base), so the best-effort stage had been failing silently on every release since the base image moved ([#17236](https://github.com/diegosouzapw/OmniRoute/pull/17236))
- **fix(dashboard):** Keep the endpoint URL in copied media-playground cURL commands. ([#17238](https://github.com/diegosouzapw/OmniRoute/pull/17238)) — thanks @pacocartones
- **fix(sse):** Parse native Gemini SSE candidates and token usage in buffered responses while preserving wrapped Gemini CLI events. ([#17239](https://github.com/diegosouzapw/OmniRoute/pull/17239)) — thanks @pacocartones
- **fix(batch):** Preserve quoted multiline CSV headers when mapping batch input columns. ([#17240](https://github.com/diegosouzapw/OmniRoute/pull/17240)) — thanks @pacocartones
- **fix(proxy):** Preserve HTTP/HTTPS subscription nodes using their default ports. ([#17241](https://github.com/diegosouzapw/OmniRoute/pull/17241)) — thanks @pacocartones
- **fix(proxy):** Preserve proxy subscription labels containing malformed percent escapes without losing sibling nodes. ([#17242](https://github.com/diegosouzapw/OmniRoute/pull/17242)) — thanks @pacocartones
- **fix(providers):** register `deepseek-v4.1-flash` and `deepseek-4.1-flash` as aliases for `deepseek-flash` so combo targets and direct requests resolve vision capabilities ([#17243](https://github.com/diegosouzapw/OmniRoute/pull/17243)) — thanks @PrinceThummar011; fixes #17180
- **fix(batch):** Require a content mapping before applying CSV batch mappings; a message role alone no longer satisfies the content requirement. ([#17244](https://github.com/diegosouzapw/OmniRoute/pull/17244)) — thanks @pacocartones
- **fix(sse):** ignore non-object events in buffered OpenAI SSE. ([#17245](https://github.com/diegosouzapw/OmniRoute/pull/17245)) — thanks @pacocartones
- **fix(sse):** preserve refusal deltas in buffered OpenAI SSE. ([#17246](https://github.com/diegosouzapw/OmniRoute/pull/17246)) — thanks @pacocartones
- **fix(capabilities):** resolve `modelCompatOverrides.supportsVision` for custom openai-compatible nodes addressed by their routing prefix, not only by their internal node id — an image request to a prefix-addressed combo target was rejected as vision-incapable even though the operator had flagged the model ([#17252](https://github.com/diegosouzapw/OmniRoute/pull/17252)) — thanks @dankarization
- Prevent stale initial provider connection, node and proxy configuration responses from updating a different provider page. ([#17253](https://github.com/diegosouzapw/OmniRoute/pull/17253)) — thanks @pacocartones
- **fix(antigravity):** Keep Claude tool results ahead of merged user text when translating to Gemini-format requests, so Claude models served through Antigravity no longer fail Anthropic's `tool_result`-first ordering rule ([#17256](https://github.com/diegosouzapw/OmniRoute/pull/17256)) — thanks @edosulai
- **fix(translator):** hoist a nested `tool_result.content[].cache_control` onto the `tool_result` block on the two paths [#15647](https://github.com/diegosouzapw/OmniRoute/pull/15647) could not reach — the openai-compat passthrough (`filterToOpenAIFormat` early-returned `role:"tool"` messages untouched, so the marker survived into a downstream gateway's Claude re-translation) and the direct openai→claude translation (both `tool_result` construction sites copied content verbatim); Anthropic rejects the nested form with `cache_control may not be specified within 'tool_result.content'`, which was 400ing every agentic turn whose history carried a cached tool result ([#17257](https://github.com/diegosouzapw/OmniRoute/pull/17257)) — thanks @patrykkopycinski
- **fix(compression):** the headroom engine now compacts Anthropic `tool_result` content — string content and nested text parts were skipped, so tool outputs from Claude Code traffic were never compacted; shared `contentTextChunks` / `mapContentPartText` helpers feed crush, collect and reconstruct, keeping the CCR restore path symmetric (#17258). — thanks @DarknessKiller
- **fix(pricing):** price `claude-sonnet-5-5` on the `cc` provider — `cc/claude-sonnet-5-5` requests were logged with no cost because the `cc` pricing table had no entry for it; it now uses the Sonnet 5.5 list price ($2 / $10 per MTok, cache read $0.20) ([#17260](https://github.com/diegosouzapw/OmniRoute/pull/17260)) — thanks @aminHZDEV
- **fix(api):** a key restricted to `cc/*` no longer lists other providers' `claude-*` models (`dva/…`, `ddgw/…`) in `/v1/models` that it cannot call, and now lists the `no-think/` variants it can call ([#17261](https://github.com/diegosouzapw/OmniRoute/pull/17261)) — thanks @aminHZDEV
- fix(dashboard): Provider Quota no longer reports "No Providers Connected" when the connection list is rejected with 401 (requireLogin=false installs, desktop app) (#17262, #17877) ([#18194](https://github.com/cheaper-inference/OmniRoute/pull/18194))
- **fix(usage):** stop the AsyncLocalStorage store from pinning the captured provider request body after its dispatch settles — `runWithCapture()` (`open-sse/utils/providerRequestLogging.ts`) now stores a small holder in the store and empties it in its `finally`, so sockets and streams that outlive the dispatch no longer keep the `Capture` and its multi-megabyte `latest` body (3–10 MB for vision traffic with base64 images) reachable. chatCore keeps the `Capture` through its own closure, so `latest()`/`body()` still return the exact wire body after the dispatch (signature-failure diagnostics, call-log request body); late callbacks from a settled dispatch cannot capture anything or overwrite a fresh retry. Refs #17268. ([#17269](https://github.com/cheaper-inference/OmniRoute/pull/17269)) — thanks @LazyGatto
- **fix(dashboard):** Decode percent-encoded combo route params (`auto%2Ffast`) so the `auto/*` Control Center page renders instead of showing `COMBO_007`, and the breadcrumb reads the decoded name (#17271, #17272 — thanks @afonsoft)
- **fix(mcp):** `/api/mcp/*` now validates the `Origin` header for every caller, as the MCP Streamable HTTP spec requires (present and invalid → 403). Before, only dashboard-session writes were checked, so a request carrying an API key, the local CLI token, or no credential at all (loopback with login disabled) reached the MCP transport whatever its `Origin` said. Clients that send no `Origin` (the OmniRoute CLI, SDK and IDE clients) are unaffected. A browser `Origin` is accepted when it is OmniRoute's own public origin (configured base URL, trusted reverse proxy's forwarded host, direct loopback/LAN IP) or `localhost` / `127.0.0.1` / `[::1]` on any port; a browser MCP client served from any other site now gets 403 with a JSON-RPC error body (`error.data.code: "INVALID_ORIGIN"`). Refs [#17131](https://github.com/diegosouzapw/OmniRoute/issues/17131) ([#17273](https://github.com/diegosouzapw/OmniRoute/pull/17273)) — thanks @HouMinXi
- **fix(release):** the post-publish verifier (`scripts/release/verify-published.mjs`) proves the published package by the installed CLI banner and the packaged `dist/package.json` and requires a healthy boot — `/api/monitoring/health` no longer reports a version, which made the check time out on every good release ([#17274](https://github.com/diegosouzapw/OmniRoute/pull/17274))
- **fix(calls):** a streamed reply that carried visible text is no longer logged as an empty reply — the call log's content-presence flag now reports it ([#17277](https://github.com/diegosouzapw/OmniRoute/pull/17277)) — thanks @maxmad64bis
- **fix(providers):** preserve an existing compatible node’s reserved prefix while its live built-in has no active configured connections. Active built-ins retain precedence during cooldown/auth failures; new reserved-prefix nodes remain rejected and retired providers remain blocked. Boot warnings and a derived `prefix_shadowed` dashboard diagnostic identify collisions without changing stored credential health or activation (#17278). ([#17339](https://github.com/cheaper-inference/OmniRoute/pull/17339))
- **fix(sse):** a direct chat-completions streaming turn that spent tokens but produced no usable text is now replayed through the normal credential path (opt-in `FLUSH_EMPTY_RETRY_ENABLED`) instead of passing because the upstream reported usage ([#17280](https://github.com/diegosouzapw/OmniRoute/pull/17280)) — thanks @maxmad64bis
- **fix(usage):** Enforce the pending-request byte ceiling on every mutation path and account for live request-logger stream chunks by request id with owned snapshots, synced to the pending map in 64 KiB batches (and flushed when the pipeline payloads are read) so the per-chunk cost stays constant; late chunks no longer attach to another request after the original finishes (#17283) ([#17282](https://github.com/diegosouzapw/OmniRoute/pull/17282)) — thanks @hummern / @turbolego
- **fix(sse):** keep server-restored signed thinking blocks intact in the trailing assistant/tool_result cycle — `prepareClaudeRequest` rewrote them to the redacted placeholder, which Anthropic rejects with a "cannot be modified" 400 on the OpenAI-to-Claude path; restored blocks are now marked by provenance (a `WeakSet`, nothing serializable) and only protected in the active tool cycle, completed history is unchanged (Refs [#17324](https://github.com/diegosouzapw/OmniRoute/issues/17324), [#17288](https://github.com/diegosouzapw/OmniRoute/pull/17288)) — thanks @mrnicholasbcarter-code
- **fix(sse):** Preserve independent `reasoning` and `reasoning_content` fields through streamed PII and credential redaction, and flush only buffered tails without replaying metadata or tool arguments. Refs #17291. ([#18363](https://github.com/cheaper-inference/OmniRoute/pull/18363))
- fix(sse): Retain raw credential fragments until safe boundaries, preserve overflow continuation for tokens/images/private keys, and fail closed at stream buffer limits instead of mixing choices (Refs #17291). ([#18363](https://github.com/cheaper-inference/OmniRoute/pull/18363))
- Signal upstream read failures after streamed content as a sanitized transport error instead of a clean end of stream, preserving pre-first-byte error frames and client cancellation. ([#17341](https://github.com/cheaper-inference/OmniRoute/pull/17341))
- **fix(tools):** preserve the client-declared `Read` path field (`path`, `filePath`, or `file_path`) in streaming and JSON responses, including strict schemas that reject extra aliases. Resolve `Read`/`read` against the original client schema, preserve valid declared values, and retain the legacy fallback when no recognizable schema is available; PDF page cleanup follows the selected path ([#17295](https://github.com/diegosouzapw/OmniRoute/pull/17295), fixes [#17294](https://github.com/diegosouzapw/OmniRoute/issues/17294)). — thanks @70nyIT
- **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 model in the same pass rebuilt the whole synced vision-verdict map (every synced catalog, re-parsed). With ~6,300 synced models and ~1,400 discovered windows that differ from the catalog, every pass took ~10.7 s, and since #11016 a pass runs after every synced catalog write. The pass now invalidates once at the end and skips rewriting an override that already holds the discovered window: ~0.5 s for the first pass, ~0.35 s for a pass that changes nothing; `refreshedAt` therefore keeps the time of the last real write instead of moving on every pass ([#17296](https://github.com/diegosouzapw/OmniRoute/pull/17296)) — thanks @fouadSalkini
- fix(usage): preserve positive prompt cache-read counts when earlier compatibility fields report zero (#17297 ([#17327](https://github.com/cheaper-inference/OmniRoute/pull/17327)) — thanks @huangyoudashi).
- **fix(cursor):** bridge Cursor's native Grep to a declared tool whose `pattern` carries `minLength`/`maxLength`. The bridge only accepted a bare `{ type: "string" }` property, so a real MCP grep tool (`pattern: { type: "string", minLength: 1 }`) was discarded and every Cursor Grep ended in the fail-closed rejection "Tool not available in this environment". `selectProperty` now receives the literal it is about to send and `propertyAcceptsString` validates the bounds against it; bounds the value violates, and any validation keyword the bridge does not implement (`pattern`, `format`, `$ref`, …), still fail closed. ([#17298](https://github.com/cheaper-inference/OmniRoute/pull/17298)) — thanks @Junior-HJ
- **fix(sse):** the intra-URL retry now honors an upstream `Retry-After` hint inside the 4s retry window and adds equal jitter to the 429 and WAF-400 backoffs, so concurrent clients no longer retry in lockstep after a fixed 2s wait ([#17306](https://github.com/diegosouzapw/OmniRoute/pull/17306)) — thanks @mayankjaiswal1707-spec
- **fix(compression):** Responses API bodies keep their newest tool output and engine-injected messages — `lite` no longer truncates the trailing `function_call_output` (it now counts as the current turn), and messages an engine injects (such as the CCR protocol instruction) are restored as new input items instead of being dropped (#17310). — thanks @DarknessKiller
- **fix(providers):** ChatPlayground follows the current web API — vendor-prefixed azure model ids (`provider/modelName` from `/api/models`), per-endpoint payload contract, and CITATIONS line stripping for perplexity ([#17312](https://github.com/diegosouzapw/OmniRoute/pull/17312)) — thanks @james-martinez
- **feat(providers):** ChatPlayground model routing is now dynamic — requests resolve against the live unauthenticated `GET /api/models` catalog (10-minute cache, single-flight, offline fallback to the bundled catalog) instead of a static model list, so new/retired models and endpoint moves are picked up without shipping a refreshed list ([#17312](https://github.com/diegosouzapw/OmniRoute/pull/17312)) — thanks @james-martinez
- **fix(cli):** Resolve the cached tray package's entry file before importing it and unwrap its CommonJS constructor correctly, preventing tray startup from failing during module loading (#17315 ([#17328](https://github.com/cheaper-inference/OmniRoute/pull/17328)) — thanks @phoriah).
- **fix(cli):** Preserve safe tray installation, import and constructor failure diagnostics in the existing crash log consulted by `omniroute doctor`, including hidden workers whose stderr is discarded (#17315). ([#17328](https://github.com/cheaper-inference/OmniRoute/pull/17328))
- **fix(providers):** Warn that direct Poolside Platform inference ends on 2026-10-31, with the official announcement and a clear distinction from independently hosted gateway models ([#17331](https://github.com/cheaper-inference/OmniRoute/pull/17331), closes #17318)
- Stop fabricating the #5312 `redacted_thinking` placeholder for adaptive-thinking replays and drop rejected/store-restored thinking blocks without re-injecting it — a mismatched blob re-triggers the "latest assistant message cannot be modified" 400 (#17324). ([#18102](https://github.com/cheaper-inference/OmniRoute/pull/18102)) — thanks @patrykkopycinski / @mrnicholasbcarter-code
- Bind signed-thinking continuity store entries to the serving connection id: thinking signatures are scoped to the producing OAuth org, so a block captured on one Claude org must never be replayed on another connection of the same provider+model (#17324). ([#18102](https://github.com/cheaper-inference/OmniRoute/pull/18102)) — thanks @patrykkopycinski / @mrnicholasbcarter-code
- Emit the #15534 privacy-safe THINKING_SIGNATURE structural diagnostics for the "cannot be modified" thinking-block 400 variant as well, not just the invalid-signature variant. ([#18102](https://github.com/cheaper-inference/OmniRoute/pull/18102)) — thanks @patrykkopycinski / @mrnicholasbcarter-code
- **fix(api):** Fixed compatible-provider nodes publishing stacked model ids (`<prefix>/<prefix>/<model>`): custom-model and validate-and-add writes now store the bare upstream id, the `/v1/models` catalog collapses any stored prefix stack before emission, and embedding endpoint routing resolves overrides keyed by the bare id. ([#17334](https://github.com/diegosouzapw/OmniRoute/pull/17334)) — thanks @woodsonl
- **fix(discovery):** show why a model discovery fell back to the built-in catalog — a degraded discovery response now carries the upstream failure reason, and the import dialog, the model picker and the connection test say the list comes from the fallback catalog and why ([#17342](https://github.com/diegosouzapw/OmniRoute/pull/17342)) — thanks @maxmad64bis / @marian-paun
- Keep the original Anthropic thinking error when recovery would create an empty assistant message or resend an identical placeholder, and restrict the recovery matcher to the latest assistant message (#17324). ([#18102](https://github.com/cheaper-inference/OmniRoute/pull/18102)) — thanks @patrykkopycinski / @mrnicholasbcarter-code
- **fix(proxy-registry):** a pool member whose observed exits stay inside one IPv4 range is now ranked under that range, so two members sharing a range no longer serve one after the other — order only, nobody is ever excluded, and the default order is unchanged ([#17347](https://github.com/diegosouzapw/OmniRoute/pull/17347)) — thanks @maxmad64bis
- **fix(opencode):** Custom OpenCode provider catalogs now join names and prices through their public routing prefixes while preserving nested model IDs from canonical providers (#17369 - thanks @fabioKoruja00)
- **fix(combo):** combo image requests no longer reject provider targets with unknown vision metadata. Confirmed vision-capable targets remain preferred; explicitly text-only models remain blocked, and WorkBuddy DeepSeek V4.1 Flash receives provider-scoped vision support (#17375 - thanks @fabioKoruja00)
- **fix(cursor):** Cursor Grok 4.7 now resolves to its 256k standard window instead of the provider's 200k default (an authoritative provider window, no extra static catalog row). Dropping the `max` effort tier, which Cursor's docs do not list, is opt-in behind the new `CURSOR_GROK_47_DROP_MAX_EFFORT` flag (default off): when on, the `grok-4.7-max` fallback alias leaves the catalog and a `max` request is sent without an effort parameter, while Grok 4.5/4.6 keep their tiers; when off, `max` stays offered and forwarded as before (#17386, [#17388](https://github.com/cheaper-inference/OmniRoute/pull/17388) — thanks @fidelix)
- **fix(dashboard):** the Combo Control Center's "Configured targets" card now shows the configured model and the pinned account in full. The row used to render the step label as its headline — and for steps created by the combo builder that label is the account name — so the model was never shown, while the account itself was cut to a truncated connection id (`abc1234…`). The model (`provider/model`, the combo name for nested combos, `provider/pattern` for wildcard targets) is now the headline, and the account is resolved from the connection row (name → displayName → email), falling back to the stored step label and finally to the full, untruncated connection id. ([#17774](https://github.com/cheaper-inference/OmniRoute/pull/17774)) — thanks @skygunner
- **fix(providers):** keep `agy` and `antigravity` hidden-model visibility independent in chat dispatch and in the combo visibility check for models without a known provider id, make `DELETE /api/provider-models?all=true` (**Clear All Models**) also remove the provider's per-model compat overrides (upstream headers, vision and protocol settings, not only the hidden flags), and expose the **Clear All Models** action on OAuth providers whenever synced models or visibility overrides exist (refs [#17398](https://github.com/diegosouzapw/OmniRoute/issues/17398), [#17400](https://github.com/diegosouzapw/OmniRoute/pull/17400) — thanks @ailuvu-art)
- **fix(usage):** Bound pending-request retention: new validated `MAX_PENDING_DETAILS` (default 5000) caps pending details in the 5-minute sweep, payload-pruned stale pending entries are physically removed about one sweep interval after being marked stale (so a request older than `MAX_PENDING_REQUEST_AGE_MS` now leaves the dashboard pending list instead of staying forever), and the pending-id correlation map is bounded on insert and cleaned when its owning detail is evicted (#17284) ([#17405](https://github.com/diegosouzapw/OmniRoute/pull/17405)) — thanks @turbolego / @hummern
- **fix(providers):** OpenCode Zen's free `mimo-v2.5-free` id is deprecated upstream (`410 Model mimo-v2.5-free has been deprecated. Use mimo-v2.6-flash-free instead.`), but it was still an active candidate, so Auto could keep selecting a model the upstream refuses. The Zen registry now advertises `mimo-v2.6-flash-free` and keeps the old id as a compatibility alias, the free-model catalog lists the replacement, and clients pinned to `mimo-v2.5-free` are forwarded to the supported id by the built-in deprecation table — the same rotation recipe as the DeepSeek V4 → V4.1 free snapshot. ([#17788](https://github.com/diegosouzapw/OmniRoute/pull/17788)) - thanks @skygunner
- **fix(sse):** the flush-empty retry loop (`FLUSH_EMPTY_RETRY_ENABLED`, opt-in) now also covers direct, non-translated turns, and a turn whose whole output is draft/system-echo markup (`<summary>`, `<analysis>`, `<system-reminder>`) counts as an empty turn instead of a normal one. The loop used to be armed for translated streams only, so a direct openai→openai turn never reached it, and the draft skeleton a model emits instead of an answer was classified as usable content — the client stripped the tags, was left with an empty turn and killed the stream. The recovery stays opt-in and bounded: the turn is retried on another connection within the existing retry budget, and a turn the judge cannot summarize still passes through unchanged. A block carrying 1000+ characters of content (the Claude Code compaction reply shape, same threshold as the system-preamble stripper) still counts as a real answer and is never retried. Behavior change: a whitespace-only content turn no longer counts as content, so it now falls through to the reasoning-only and zero-valuable-chunk checks (a reasoning-only turn with blank text becomes retryable). With the flag on, the bounded pre-first-byte buffering now also applies to direct streaming paths. ([#17791](https://github.com/diegosouzapw/OmniRoute/pull/17791)) - thanks @skygunner
- **fix(cli):** MCP-backed CLI commands (`mcp call`, `combo suggest`, `compression`, `oneproxy`, `resilience`, `skills`) no longer require a `Mcp-Session-Id` after `initialize`, so they keep working against a stateless Streamable HTTP handler: they send `notifications/initialized`, attach the session header only when the server issued one, and `DELETE` the session (tolerating 405) only after the response has been fully read. `tools/call` is never retried, so a slow or failing tool is no longer run twice. `mcp call` now uses the shared client. Its `tools/call` still has no client-side deadline by default, as before; an explicit `--timeout`, else `OMNIROUTE_HTTP_TIMEOUT_MS`, limits the whole call, from sending it until the result (or the last `--stream` frame) has been read, and on expiry it prints `MCP tools/call timed out after <ms>ms`, exits 1 and sends a best-effort `notifications/cancelled` to a sessionful server. `initialize` and `notifications/initialized` use `--timeout`, else `OMNIROUTE_HTTP_TIMEOUT_MS`, else 30 s, per attempt and until the response starts; `initialize` is tried up to 3 times, so a server that never answers fails after about 92 s by default. `--timeout 0` means no client-side deadline, while `OMNIROUTE_HTTP_TIMEOUT_MS=0` counts as unset; values above 2147483647 ms are clamped to that limit instead of aborting after 1 ms, and a `--timeout` or `OMNIROUTE_HTTP_TIMEOUT_MS` value that is not a plain non-negative integer (blank, surrounding whitespace, leading zeros such as `00`, signs, units such as `300ms`, decimals) prints a warning and is ignored; it also honours `--base-url`, `--context` and `--api-key`. Session `DELETE` uses a fixed 3 s timeout; a tool result with `isError` now prints `MCP error: <text>` to stderr and exits 1 (previously stdout, exit 0); `--scope` was never used and now prints a deprecation warning. HTTP failures read `MCP <method> failed: HTTP <status> — <body>` and keep `err.status` (the compression REST fallback on 404/501 is unchanged); JSON-RPC errors keep `err.code`. No `MCP-Protocol-Version` header is sent, and the unused `X-MCP-Scopes` header is removed. Refs [#17131](https://github.com/diegosouzapw/OmniRoute/issues/17131) ([#17793](https://github.com/diegosouzapw/OmniRoute/pull/17793)) — thanks @HouMinXi
- **fix(perplexity-web):** an unmapped model name now returns a local `404 model_not_found` before any upstream call, instead of being forwarded to Perplexity as a raw `model_preference` ([#17822](https://github.com/diegosouzapw/OmniRoute/pull/17822)) — thanks @HouMinXi
- fix(responses): make the synthetic response.in_progress keepalive a complete, spec-valid event so Vercel AI SDK clients no longer abort the stream (#17825) ([#18197](https://github.com/cheaper-inference/OmniRoute/pull/18197))
- **fix(sse):** Codex remote compaction no longer trips the content-stall watchdog mid-compaction — `response.compaction.compacting` progress frames now restart the stall budget like reasoning frames, while keepalive-only streams are still aborted (#17826) ([#17940](https://github.com/cheaper-inference/OmniRoute/pull/17940)) — thanks @skygunner
- **fix(chatgpt-web):** the clean-room bridge resolves ChatGPT's Sentinel integrity module through the page's own bundle runtime instead of hand-instantiating its factory against a pinned minified dependency id. chatgpt.com assigns those ids per deployment, so when the uuid helper behind the sentinel payload moved from `TI` to `DD` (and into another chunk), every `chatgpt-web` turn failed with `[502] ChatGPT Web first-party bridge module failed to load: pageerror: Unsupported ChatGPT integrity dependency: DD` while the stored cookie and storage state stayed valid. No id is pinned and no dependency is stubbed now, and resolution stays lazy because a module script's `load` event fires before a pending top-level `await` settles ([#17833](https://github.com/diegosouzapw/OmniRoute/pull/17833)) ([#17840](https://github.com/cheaper-inference/OmniRoute/pull/17840)) — thanks @skygunner
- **fix(xai):** `xaiCompletedToClaudeJson` now restores each tool-call name to the spelling the client declared instead of upgrading known Claude Code tools to PascalCase, matching the `/v1/messages` translate paths from #17119; callers without an original request are unchanged ([#17835](https://github.com/diegosouzapw/OmniRoute/pull/17835)) — thanks @AndresDiagoM
- **fix(perplexity-web):** align the Perplexity Firefox user agent and TLS profile on Firefox 151 (wreq-js 3.2.0) and correct the connection validator's drifted Firefox 152 UA ([#17838](https://github.com/diegosouzapw/OmniRoute/pull/17838)) — thanks @HouMinXi
- **fix(chatgpt-web):** the clean-room conversation body carries the first-party ability slider as a `thinking_effort` field (`standard` / `extended` / `max`) on the family's `-thinking` slug, instead of collapsing the three positions into `system_hints: ["reason"]` — every Medium/High/Extra-High request used to be byte-identical, so the selected depth was silently ignored. `system_hints: ["reason"]` now only travels for Free's Think toggle, and the Pro lane keeps its captured depth (`standard`, or `extended` for GPT-5.5 Pro) ([#17840](https://github.com/diegosouzapw/OmniRoute/pull/17840)) — thanks @skygunner
- **fix(gemini):** `/v1beta/models/*:streamGenerateContent` now forwards the trailing OpenAI usage-only chunk (`choices: []`) as a Gemini `usageMetadata` chunk, so `@google/genai` clients get token usage from OpenAI-style upstreams; `cachedContentTokenCount` (and `thoughtsTokenCount` on the non-streaming Gemini-family envelope) is now reported on every `/v1beta` path ([#17842](https://github.com/diegosouzapw/OmniRoute/pull/17842)) — thanks @AndresDiagoM / @skygunner
- **fix(audio):** `/v1/audio/translations` no longer echoes an upstream provider's raw non-JSON error body (HTML pages, stack traces, echoed credentials) to the client and now carries CORS on upstream errors, using the shared `upstreamErrorResponse` that speech and transcription adopted in #15159 ([#17844](https://github.com/diegosouzapw/OmniRoute/pull/17844)) — thanks @AndresDiagoM / @skygunner
- **fix(opencode):** free models answer again on the gated `https://opencode.ai/inference/v1` lane: that lane 403s a real API key with `Model access is disabled` and serves the anonymous `Bearer public` credential instead (measured 2026-10-21 on Chat Completions and Responses), so a gated request now authenticates with the lane credential while paid models keep the operator key on the keyed `/zen/v1` lane; `OPENCODE_FREE_TIER_CREDENTIAL` overrides it (`key` restores the operator key, `none`/`off` sends no credential header) ([#17848](https://github.com/diegosouzapw/OmniRoute/pull/17848)) — thanks @XcubeJava
- **fix(ci):** Reject stale or incomplete direct dependency versions before native install probes, including borrowed test runners that differ from the candidate lockfile; retain the lockfile digest in the verification receipt (#17854).
- **fix(providers):** a failed Codex client-version refresh no longer disappears silently. `refreshCodexClientVersion()` swallowed every failure with a bare `catch {}`, so a deployment whose egress blocks `registry.npmjs.org` kept advertising the pinned `DEFAULT_CODEX_CLIENT_VERSION` with no log, no counter and no surfaced state — the live `/backend-api/codex/models` catalog simply came back short and nothing explained why (#16714). The refresh now warns once per failed attempt with the concrete reason (thrown error message, non-ok HTTP status, or schema mismatch) and records which source the next upstream call will use: `override` (`CODEX_CLIENT_VERSION`), `registry` (npm metadata applied), `pinned` (the registry answered but is not newer than the built-in floor, a healthy state that neither warns nor flags the catalog as degraded) or `fallback` (last refresh failed). `getCodexClientVersionSource()` exposes it, and the Codex discovery response now carries `discovery.clientVersion = { version, source, degraded? }` so an operator can see a degraded catalog from the API instead of inferring it from missing models. Behaviour is otherwise unchanged: failures still fall back to the last-known-good version and are still retried after the bounded delay. ([#17855](https://github.com/cheaper-inference/OmniRoute/pull/17855)) — thanks @Junior-HJ
- **fix(i18n):** Spanish UI strings left in English or stale are now translated from the current English source ([#17868](https://github.com/diegosouzapw/OmniRoute/pull/17868)) — thanks @DeadY2J
- **fix(tls):** browser fingerprints (TLS profile, User-Agent and client hints) now come from the newest profile the installed wreq-js ships, resolved at request time, instead of literals frozen in source. The shared TLS session and the Codex Responses WebSocket move off their pinned chrome_124 / chrome_142 profiles, and grok-web, claude-web, perplexity-web, notion-web and lmarena send a User-Agent that matches their TLS fingerprint. An unusable wreq-js catalog fails the request that needs it instead of the module load. The claude-web clearance cache is keyed by the browser version, so a wreq-js upgrade drops cookies minted under the old one ([#17869](https://github.com/cheaper-inference/OmniRoute/pull/17869)) — thanks @HouMinXi
- **fix(resilience):** Combos keep per-account cooldown reasons and deadlines through quota-aware ordering and connection-aware expansion (round-robin, fusion, pipeline included), so a pool emptied only by cooldowns returns the existing 503 `all_targets_cooling_down` with a bounded `Retry-After` instead of a false 404/400 ([#17880](https://github.com/cheaper-inference/OmniRoute/pull/17880)) — thanks @AiCaptain357
- **fix(sse):** quota preflight now enforces an operator-configured remaining-quota cutoff even right after a successful request — a recent-success "healthy" mark no longer lets a connection keep serving below its configured reserve until an upstream 429; unconfigured exhaustion hints still yield to recent success (#14359) ([#17884](https://github.com/diegosouzapw/OmniRoute/pull/17884) — thanks @insoln)
- **fix(combo):** Clear completed sets' safety timers before retrying, preventing stale timeout warnings while preserving active-set timeout protection ([#17885](https://github.com/cheaper-inference/OmniRoute/pull/17885)) — thanks @AiCaptain357
- **fix(combo):** Keep retrying combos nonterminal and record the actual final response status in traces ([#17888](https://github.com/cheaper-inference/OmniRoute/pull/17888)) — thanks @AiCaptain357
- fix(translator): keep bare leaf names for the Responses default `functions` namespace on Chat upstreams so Codex `exec`/`wait` are no longer renamed to `functions__exec` (#17890) ([#18198](https://github.com/cheaper-inference/OmniRoute/pull/18198))
- **fix(usage):** Prevent updates and completions with an unmatched explicit ID from affecting another pending request ([#17891](https://github.com/cheaper-inference/OmniRoute/pull/17891)) — thanks @AiCaptain357
- **fix(logs):** Correct in-memory request preview timestamps, completed duration and related-attempt ordering ([#17894](https://github.com/cheaper-inference/OmniRoute/pull/17894)) — thanks @AiCaptain357
- **fix(translator):** GitHub Copilot Responses-API reasoning summaries are no longer duplicated, nor leaked to Claude clients as assistant text on tool-call turns — reasoning dedupe now also matches by `output_index` (Copilot rotates the reasoning item id), and FIX B only synthesizes a text block for reasoning-only replies (#17898 — thanks @lkolchin)
- **fix(logs):** Discard expired in-memory previews during related-request polling while preserving saved history and real retries ([#17900](https://github.com/cheaper-inference/OmniRoute/pull/17900)) — thanks @AiCaptain357
- **fix(combo):** Interleave hedged launches across model groups, limit the identical-error early stop to HTTP 400 under serial dispatch, isolate thrown branch failures so surviving hedged branches keep running, and stop counting a local `409 turn_in_progress` against the account cooldown ([#17901](https://github.com/cheaper-inference/OmniRoute/pull/17901)) — thanks @edosulai
- **fix(oauth):** Accept Cursor's permission-based `grok_` user API keys alongside legacy `crsr_` keys through the shared Cursor key validator, including the Agent image CLI env selection ([#17905](https://github.com/cheaper-inference/OmniRoute/pull/17905)) — thanks @stanleytejakusuma
- fix(providers): store non-streaming Gemini/Vertex thought signatures under the connection+family namespace so tool-call history is replayed instead of repeating the call (#17912) ([#18201](https://github.com/cheaper-inference/OmniRoute/pull/18201))
- **fix(sse):** the Antigravity CLI profile now matches the native `agy` wire image — the User-Agent reports the real host in Go's `windows`/`amd64` dialect plus the build changelist (`cl=`) instead of the pinned `darwin/arm64`, `streamGenerateContent` no longer sends `Accept: text/event-stream` or `x-goog-user-project` on the consumer path, and `loadCodeAssist` sends the native 38-byte `{"metadata":{"ideType":"ANTIGRAVITY"}}` body. The IDE profile keeps its `darwin/arm64` pin and numeric protobuf enums. Also drops the never-read `ANTIGRAVITY_USER_AGENT` env knob. ([#17913](https://github.com/cheaper-inference/OmniRoute/pull/17913)) — thanks @Junior-HJ
- fix(intelligence): fall back to the lmarena-ai Hugging Face dataset when the Arena ELO scraper API is empty and expose lastError/activeSource in the sync status (#17922) ([#18186](https://github.com/cheaper-inference/OmniRoute/pull/18186))
- **fix(oauth):** WorkBuddy login now starts the Device Code flow in the dashboard — `workbuddy` was missing from the OAuth modal's `DEVICE_CODE_PROVIDERS`, so the modal called `/authorize`, got `authUrl: null` and failed with "browser OAuth is not available". A new unit test checks that every `device_code` provider in the OAuth registry is listed in the modal ([#17836](https://github.com/diegosouzapw/OmniRoute/issues/17836)) ([#17926](https://github.com/cheaper-inference/OmniRoute/pull/17926)) — thanks @adrian-cancio
- **fix(docker):** pin the Docker images to patched runtimes so they reject the compromised eMudhra intermediate CAs — `Dockerfile` `node:26.12.1-trixie-slim` (was the floating `node:26-trixie-slim`), `Dockerfile.bun` `oven/bun:1.4.4-slim`, and the devin-bridge `compose.yml` + `Dockerfile` `node:26.12.1-bookworm-slim` (was `26.0.0`); a unit test fails on any floating tag or pin below Node 26.12.1 / Bun 1.4.4 ([#17927](https://github.com/cheaper-inference/OmniRoute/pull/17927)) — thanks @hartmark
- **fix(nvidia):** Replace the retired `nvidia/nemotron-3-super-120b-a12b` (404 since 2026-10-20) with its replacement `nvidia/nemotron-3.5-super-vl-120b-a12b` in the NVIDIA catalog and Antigravity bridge auto groups ([#17928](https://github.com/diegosouzapw/OmniRoute/pull/17928)) — thanks @DeadY2J
- **fix(cache):** Count semantic-cache hits by exact signature so one hit no longer increments unrelated entries sharing an API-key prefix; preserve global metrics and use the existing unique index ([#17934](https://github.com/cheaper-inference/OmniRoute/pull/17934)) — thanks @xiaoyaner0201
- **fix(audio):** `/v1/audio/speech`, `/v1/audio/transcriptions` and `/v1/audio/translations` now carry CORS headers on the sanitized 500 returned when the upstream request itself fails (DNS, connection or TLS error), matching their upstream-error responses since #15159 (#17938) — thanks @AndresDiagoM
- **fix(codex):** registered Codex image models (`gpt-image-2`, `gpt-image-2.5-*`) are no longer filtered by the synced Codex chat inventory, which never lists them — `/v1/images/generations` stops failing with "No credentials for image provider: codex" once a Codex inventory is synced (#17942). — thanks @adevwithpurpose
- **fix(translator):** `fixMissingToolResponses` scans all consecutive tool responses and inserts empty responses for any unanswered `tool_call_id` in a multi-call assistant turn or trailing assistant turn, preventing 400 errors from strict upstreams such as DeepSeek; on Claude-shaped payloads the missing `tool_result` blocks join the existing results message instead of a second consecutive user message (#17946). ([#17947](https://github.com/cheaper-inference/OmniRoute/pull/17947)) — thanks @fidelix
- **fix(proxy):** Prevent quota timeouts from interrupting active SOCKS streams while preserving connection reuse and pool limits ([#17949](https://github.com/cheaper-inference/OmniRoute/pull/17949)) — thanks @AiCaptain357
- **fix(api):** Return a sanitized JSON 400 with a `POST /v1/search` hint when a search-only provider is used for chat through `/v1/chat/completions`, `/v1/messages` or `/v1/responses`; retain correlation IDs, preserve the error in streams that have already started and release request admission capacity. Fixes #17858 ([#17953](https://github.com/cheaper-inference/OmniRoute/pull/17953)) — thanks @manjughosa / @MumuTW
- **fix(compression):** caveman compression keeps the latest user turn intact when earlier turns exist (#17954) — thanks @levinkin
- **fix(dev):** macOS development startup no longer fails with `EBADF` from Next's TypeScript CLI subprocess and static-path worker (in-process TypeScript config and thread workers, macOS dev only), and combo test messages keep model reasoning out of the visible test output ([#17955](https://github.com/cheaper-inference/OmniRoute/pull/17955)) — thanks @ahmedgo17390
- **fix(combo):** with `outputLimitAdvisory` on, the context-window check sizes each target's output by its own known output limit instead of the full `max_tokens`, so an oversized `max_tokens` no longer demotes a declared primary whose capped reply fits its window ([#17958](https://github.com/diegosouzapw/OmniRoute/pull/17958)) — thanks @HouMinXi
- **fix(i18n):** the feature-flags page now has a label and description for all 111 registry flags in every locale (`featureFlags.definitions` covered only 47 of 111, so the newest flags rendered raw English), the ICU syntax literals in seven flag descriptions are quoted so every catalog compiles, and the zh-CN/zh-TW catalogs get ~60 missing strings plus corrections for mistranslations ("lightweight", "aggressive", the CLI Code page title) ([#17960](https://github.com/diegosouzapw/OmniRoute/pull/17960)) — thanks @HouMinXi
- fix(providers): grok-web fast/expert labels now say Grok 4.7 and grok-cli no longer lists the removed Composer 2.5 model (#17963) ([#18192](https://github.com/cheaper-inference/OmniRoute/pull/18192))
- **fix(executors):** custom native Responses upstreams (`openai-compatible-*` with `apiType: "responses"`) keep assistant `phase: "commentary"` messages in the request history. `BaseExecutor.transformRequest` now reads the `_nativeOpenAICompatibleResponsesPassthrough` stamp and calls the input sanitizer with `dropInternalAssistantMessages: false`, matching the native Codex passthrough (#3107); translated paths and unstamped providers still drop them (fixes #17834) ([#17966](https://github.com/cheaper-inference/OmniRoute/pull/17966)) — thanks @Abelo9996
- **fix(providers):** Cloudflare Workers AI models now carry their documented context window (`llama-3.3-70b-instruct-fp8-fast` and `qwq-32b` 24,000, `mistral-7b-instruct-v0.2-lora` 15,000, `qwen2.5-coder-32b-instruct` 32,768, `deepseek-r1-distill-qwen-32b` and `llama-3.2-3b-instruct` 80,000). A combo with one of these members used to advertise no window or a larger sibling's (262,144), so clients sent prompts the 24k member rejected with 413; the combo now advertises the smallest member's window (fixes #17881) ([#17967](https://github.com/cheaper-inference/OmniRoute/pull/17967)) — thanks @Abelo9996
- fix(providers): stop injecting a 4096 max_tokens ceiling when a reasoning request carries no client cap, so long outputs such as Codex compaction are no longer truncated (#17971) ([#18212](https://github.com/cheaper-inference/OmniRoute/pull/18212))
- **fix(docker):** add a Docker-client-free `runner-cli-no-docker` target built from a shared `runner-cli-core`, keep the default `runner-cli` target backward compatible with the Docker and Compose clients, and remove Next.js trace manifests from standalone artifacts ([#17972](https://github.com/cheaper-inference/OmniRoute/pull/17972)) — thanks @seanford
- **fix(autopilot):** Improve provider and combo health autopilot accuracy for disabled inventory, informational diagnostics, connection eligibility, and account-scoped quota coverage ([#17974](https://github.com/diegosouzapw/OmniRoute/pull/17974)) — thanks @seanford
- **fix(combos):** Expose configured static image, speech, and video registry models in the Combo Builder while preserving their routing metadata ([#17975](https://github.com/diegosouzapw/OmniRoute/pull/17975)) — thanks @seanford
- **fix(api):** apply per-key `allowAutoCombos=false` to the built-in bare `auto` route as well as `auto/*`, while preserving ordinary permissions for a persisted combo literally named `auto` ([#17978](https://github.com/diegosouzapw/OmniRoute/pull/17978)) - thanks @seanford
- **fix(dashboard):** Paginate the combo inventory so large installations remain responsive, while keeping filter state, ordering, and drag controls consistent across pages ([#17985](https://github.com/diegosouzapw/OmniRoute/pull/17985)) — thanks @seanford
- fix(db): READONLY and exhausted transient probe errors no longer rename a healthy storage.sqlite; startup fails closed with a typed probe result (#17995) ([#18191](https://github.com/cheaper-inference/OmniRoute/pull/18191))
- **fix(models):** model discovery reports its degraded states accurately: a Codex upstream that answers with a recognised empty model list now falls back to the cached/public/local catalog with its own warning instead of "live catalog unavailable", an error envelope can no longer become a phantom `error` model, Grok Build reads the largest `context_windows` tier as the input limit (`grok-4.7-build-fast` is 500k, not 256k), and OpenCode `zen`/`go` discovery authenticates with `x-api-key` instead of a rejected Bearer token ([#17996](https://github.com/diegosouzapw/OmniRoute/pull/17996)) — thanks @HouMinXi
- **fix(oauth):** `serializeRefresh` is reentrant per rotation group, so `refreshAndUpdateCredentials` (Codex reset-credit, provider limits, quota refresh) no longer waits 60 s on its own lane when the Codex/Claude executor's `refreshCredentials()` calls `getAccessToken()`; the refresh now completes and its rotated tokens are persisted instead of the stale token being returned with `refreshed: false`. ([#18000](https://github.com/cheaper-inference/OmniRoute/pull/18000)) — thanks @rbansal42
- **fix(streams):** with `STREAM_ACTIVE_TIMEOUT_MS` unset, the active-stream watchdog no longer cuts off a target whose own declared budget is longer than the 21-minute default (1260000). GLM declares a 50-minute provider-level `timeoutMs`, and a connection can declare its own in `providerSpecificData.timeoutMs`, but both were killed at 21 minutes mid-answer with no terminal event. Such a target now gets the larger of the default and its declared budget (connection, then per-model, then provider-level) plus the one-minute margin, clamped to the timer range; targets that declare nothing keep the 21-minute bound #12913 added for endless streams. A positive `STREAM_ACTIVE_TIMEOUT_MS` is now a hard cap for every target that declared budgets cannot raise, and `0` still disables the watchdog. ([#18002](https://github.com/diegosouzapw/OmniRoute/pull/18002)) — thanks @HouMinXi
- **fix(docker):** Allow only the required Claude Code and OpenClaw lifecycle scripts under npm 12, and verify all bundled CLIs as the non-root runtime user during the build ([#18003](https://github.com/cheaper-inference/OmniRoute/pull/18003)) — thanks @jktan0504
- **fix(combo):** a streaming target that opens the response and then produces nothing no longer holds the whole combo. The per-target budget now bounds the time to the first output frame instead of the time to response headers, so a silent upstream is abandoned and the combo falls over to its next model; token generation that has started still streams to completion untouched. Output is judged with the same predicate TTFT uses, so this gateway's own keepalive frames (`response.in_progress`, `event: progress`) never count as an answer. A stream that closes without output, or loses a hedge race, goes back with its body unread, so empty-turn checks and the client see the same bytes as before. A target that declares a longer budget (connection `timeoutMs`, then the registry's per-model `timeoutMs`, then its provider-level `timeoutMs`, e.g. Codex xhigh or GLM) may wait longer for its first output, up to 180 seconds; declared budgets are whole-request budgets, so they never stretch the wait past that and never shorten it. An explicit `targetTimeoutMs`, on the combo or in the global combo defaults, still overrides all of them. ([#18004](https://github.com/diegosouzapw/OmniRoute/pull/18004)) — thanks @HouMinXi
- fix(api): stream text transform no longer treats usage.inference_geo, usage.speed, signature and request_id as answer text (#18006) ([#18206](https://github.com/cheaper-inference/OmniRoute/pull/18206))
- fix(providers): force stream:true upstream for forceStream providers even when the client body sends an explicit stream:false (WorkBuddy 11101) (#18007) ([#18212](https://github.com/cheaper-inference/OmniRoute/pull/18212))
- fix(vision): keep raw image parts for mixed vision/text-only combos instead of replacing them with descriptions or an "unavailable" stub (#18008) ([#18203](https://github.com/cheaper-inference/OmniRoute/pull/18203))
- **fix(stream):** Claude refusals reach the client — a turn that ends with `stop_reason: "refusal"` and no content is no longer rewritten into a retried 502 "empty content" error, so the client sees the refusal and its `stop_details` on both streaming and non-streaming requests (#18009). — thanks @guiguan
- **fix(models):** operator-defined custom models now appear on curated-only providers (CodeBuddy CN/intl, Kimi Web, Z.ai Web, ChatGPT Web). "Curated" only means the provider publishes no upstream model-catalog endpoint, so the dashboard already hid "Import Models" and skipped every synced-catalog loop; that flag leaked into the custom-model paths too. A row added through "Add Custom Model" was saved in the database but then dropped by `mergeProviderModelListing` (never shown in _Available Models_), by `GET /api/providers/[id]/models` (never returned to the dashboard) and by `GET /v1/models` (never routable), which made the model impossible to use. Discovery behaviour is unchanged — no sync is attempted for these providers, and only the operator's own rows are kept. ([#18018](https://github.com/cheaper-inference/OmniRoute/pull/18018)) — thanks @Junior-HJ
- **fix(providers):** Gemini requests with native `web_search` now retain caller function tools; native search remains available when no function declarations are present, and is dropped (with a warning log) when function declarations are present. ([#18020](https://github.com/cheaper-inference/OmniRoute/pull/18020)) — thanks @lorenzozanee
- **fix(guardrails):** streaming credential redaction no longer re-sends the last forwarded delta in its end-of-stream flush — `tool_calls` headers reached OpenAI Chat Completions clients twice and the accumulated arguments were invalid JSON; the `finish_reason` chunk also drains its own held-back tail so it is no longer emitted out of order (#18027) — thanks @Szqub
- **fix(providers):** WorkBuddy model discovery now sends the required client identity headers, preventing authenticated catalog requests from failing with gateway error `12403` (#18035). — thanks @erSpell
- **fix(claude):** stop replaying the hardcoded `DEFAULT_THINKING_CLAUDE_SIGNATURE` blob as `redacted_thinking` — the constant is a real encrypted thought from an unrelated session, and Claude decrypted it as its own earlier reasoning, which derailed long agent sessions. Genuine signed thinking is now kept verbatim on every turn, unsigned blocks and the placeholder are dropped, manual thinking is switched off for a request whose final `tool_use` turn has no thinking block, and the 400 signature recovery also covers Vertex and `claude` targets ([#18036](https://github.com/cheaper-inference/OmniRoute/pull/18036)) — thanks @DavyMassoneto
- **fix(resilience):** A transient credential-health probe network error, upstream outage, or upstream rate limit no longer blocks a valid connection's combo dispatch. Genuine credential failures and the opt-in handling of ambiguous probes retain their existing safeguards, while skip logs distinguish confirmed credential errors from failed probes (#18037). ([#18243](https://github.com/cheaper-inference/OmniRoute/pull/18243)) — thanks @fabioKoruja00
- **fix(embeddings):** the shared embeddings normalizer fills the `index` that Gemini's OpenAI-compatible endpoint omits on the first batch item (a proto3 default), keeping an upstream-sent index, so OpenAI SDK clients that sort by `index` no longer crash ([#18043](https://github.com/diegosouzapw/OmniRoute/issues/18043), [#18044](https://github.com/diegosouzapw/OmniRoute/pull/18044)) — thanks @TheViniAlmeida
- fix(api): control-only SSE chunks no longer log a sanitizer warning on every stream (#18047) ([#18206](https://github.com/cheaper-inference/OmniRoute/pull/18206))
- fix(providers): model sync no longer starves the event loop — the post-sync context-window reconcile is provider-scoped, trailing-debounced, skipped when no window changed, and cooperative (#18048) ([#18211](https://github.com/cheaper-inference/OmniRoute/pull/18211))
- **fix(providers):** the Codex Responses WebSocket bridge now applies per-model `upstreamHeaders` (including the `openai-responses` layer) and connection custom headers / User-Agent like the HTTP path, so a per-model `Version` override no longer gets lost on the WS upgrade and stops returning `model_not_found` ([#18050](https://github.com/cheaper-inference/OmniRoute/pull/18050)) — thanks @xiaoyaner0201
- **fix(antigravity):** a confirmed response-header timeout (`ANTIGRAVITY_PRE_RESPONSE_TIMEOUT`) now keeps its cause through the non-streaming leg and is no longer replayed across fallback URLs, Pro candidates or the same account — every Antigravity 504 now skips the same-account transport retry so the router rotates to a different account instead of doubling the wait; Codex's transient 504 retry and Antigravity 503 retry are unchanged (#18052). — thanks @Bl0ck154
- **fix(claude):** Treat Claude's `Usage credits are required for this model` 429 as a long-window quota failure instead of retrying every five seconds. Lock only the affected model when no fresh quota snapshot exists, preserve sibling-model availability and honor verified account-wide quota blockers (#18058). ([#18245](https://github.com/cheaper-inference/OmniRoute/pull/18245)) — thanks @fabioKoruja00
- fix(client): inline OMNIROUTE_BASE_PATH into client bundles so fetch/EventSource and the login page work under a subpath deploy (#18062) ([#18199](https://github.com/cheaper-inference/OmniRoute/pull/18199))
- **fix(claude):** Responses API requests with `reasoning.effort: "max"` routed to a Claude-format target now forward `max` upstream when the model supports it, instead of being clamped to `xhigh`; Claude max-effort support moves to a per-family minimum-version table (Fable/Mythos any, Opus and Sonnet 4.6+, Haiku 5.5+, Sonnet 5.5 still excluded), so Sonnet 4.5 no longer receives `max` ([#18064](https://github.com/cheaper-inference/OmniRoute/pull/18064)) — thanks @DavyMassoneto
- **fix(db):** utilization chart bucket timestamps are now emitted as ISO-8601 UTC (`YYYY-MM-DDTHH:MM:SSZ`), so the dashboard no longer shifts them by the browser's UTC offset ([#18083](https://github.com/cheaper-inference/OmniRoute/pull/18083)) — thanks @levinkin
- **fix(stream):** a stream interrupted after content is delivered ends with its explicit failure terminal instead of a silent truncation ([#18094](https://github.com/cheaper-inference/OmniRoute/pull/18094)) — thanks @maxmad64bis
- **fix(sse):** a non-JSON `data:` line no longer counts as useful stream content, so a proxy error page or a truncated fragment served under `data:` cannot satisfy the empty-turn verdict or the content watcher ([#18095](https://github.com/cheaper-inference/OmniRoute/pull/18095)) — thanks @maxmad64bis
- **fix(sse):** Preserve request-complexity hints when selecting the primary auto-combo model, matching the complexity signal already used for fallback ranking. ([#18104](https://github.com/cheaper-inference/OmniRoute/pull/18104)) — thanks @xc0derx
- **fix(sse):** Preserve harness tool calls, results and system instructions across Codex app-server requests by replaying structured history; update the bundled Codex CLI and matching client identity to 0.165.0. ([#18105](https://github.com/cheaper-inference/OmniRoute/pull/18105)) — thanks @xc0derx
- **fix(combo):** Clear the stale provider pin after a quality-rejected 200 so the next request re-resolves ([#18113](https://github.com/cheaper-inference/OmniRoute/pull/18113)) — thanks @maxmad64bis
- **fix(sse):** keep an explicit client thinking type instead of overwriting it with the connection default ([#18114](https://github.com/cheaper-inference/OmniRoute/pull/18114)) — thanks @maxmad64bis
- **fix(pool):** Serve a fresh session instead of null when the pool holds only dead sessions ([#18115](https://github.com/cheaper-inference/OmniRoute/pull/18115)) — thanks @maxmad64bis
- **fix(bootstrap):** inline comments after unquoted values in env files are now ignored, so annotating a variable no longer changes the value read at boot ([#18116](https://github.com/cheaper-inference/OmniRoute/pull/18116)) — thanks @maxmad64bis
- **fix(sse):** a top-level camel-case reasoning effort is now folded into the canonical effort field and stripped from the body sent to OpenAI-compatible upstreams, so strict gateways no longer reject the request with an unsupported-argument error ([#18117](https://github.com/cheaper-inference/OmniRoute/pull/18117)) — thanks @maxmad64bis
- **fix(zcode):** Native Coding Plan quota refusals keep their exact native code instead of being flattened into a generic 402/429, a cancelled attempt stays request-scoped (no model lockout, no provider circuit-breaker failure, no provider cooldown) so the combo can still fall back to another target, and our own graceful shutdown no longer leaves provider state behind ([#18118](https://github.com/diegosouzapw/OmniRoute/pull/18118) - thanks @megamen32)
- **fix(claude):** Claude-format targets (`claude`, `claude-code`, `anthropic-compatible-*`) are no longer cut to the default 128-tool cap, which dropped Claude Code built-ins such as Read, Write, Skill and SendMessage behind a large MCP catalog; documented and learned limits still apply. Tools the client sent keep the client's order (only gateway-injected tools are sorted), and a capped list now logs one WARN naming the dropped tools (#18120 - thanks @JulesLecuir) ([#18249](https://github.com/cheaper-inference/OmniRoute/pull/18249))
- **fix(sse):** streaming PII redaction now honors the pii-masker opt-out instead of redacting anyway ([#18121](https://github.com/cheaper-inference/OmniRoute/pull/18121)) — thanks @maxmad64bis
- **fix(codex):** preserve effort-only reasoning requests for the configured Codex model families when translating Chat Completions to Responses, without adding an unsolicited reasoning summary or encrypted-content include. Native Responses defaults and explicit client reasoning controls remain unchanged. This request-shape change has not been established as a fix for an upstream safety rejection ([#18124](https://github.com/cheaper-inference/OmniRoute/pull/18124)). — thanks @aldoeliacim
- **fix(usage):** register Muse Code subscription metadata with the usage fetchers. Surface the stored tier, activity, and payment-method fields without making a network request or presenting subscription metadata as measured quota. Correct the active/inactive message and cover metadata extraction in `tests/unit/muse-code-usage.test.ts` ([#18124](https://github.com/cheaper-inference/OmniRoute/pull/18124)). — thanks @aldoeliacim
- **fix(sse):** Purge releasable failure entries past held lockouts so the failure map stays within its cap ([#18126](https://github.com/cheaper-inference/OmniRoute/pull/18126)) — thanks @maxmad64bis
- **fix(responses):** Preserve web search call ids when replaying conversation history ([#18127](https://github.com/cheaper-inference/OmniRoute/pull/18127)) — thanks @maxmad64bis
- **fix(combo):** Apply the global per-target expansion cap when a combo sets none ([#18130](https://github.com/cheaper-inference/OmniRoute/pull/18130)) — thanks @maxmad64bis
- **fix(sse):** Strip a learned-only effort suffix back to the base model instead of sending the variant upstream ([#18131](https://github.com/cheaper-inference/OmniRoute/pull/18131)) — thanks @maxmad64bis
- **fix(combo):** a combo with set retries enabled now skips the providers and connections whose quota is durably exhausted instead of replaying them on every set; transient failures are still retried. ([#18133](https://github.com/cheaper-inference/OmniRoute/pull/18133)) — thanks @maxmad64bis
- **fix(sse):** the Codex app-server, the ChatGPT Web (Codex) runtime and the local Devin CLI bridge answer `499` (`client_disconnected`) instead of `502` while the instance is draining — a graceful restart used to be read as provider ill health, so `markAccountUnavailable` wrote `testStatus "unavailable"` into `provider_connections` (state that outlives the restart) and the combo added a model lockout, a provider circuit-breaker failure and a provider cooldown for a teardown we caused ourselves; the shared `drainAwareFailureStatus()` helper (open-sse/utils/drainStatus.ts) reads the drain flag live, applies to 5xx only and leaves a genuine upstream failure at its own status ([#18134](https://github.com/cheaper-inference/OmniRoute/pull/18134) - thanks @megamen32)
- **fix(compression):** an idle compression pool worker no longer keeps a short-lived process (a script, the eval runner, a test file) alive for its 60 s idle window. The worker is unref'd when it goes idle and ref'd again before the next job is posted, so a job in flight still holds the process; the idle termination and the server are unchanged ([#18136](https://github.com/diegosouzapw/OmniRoute/pull/18136)) — thanks @HouMinXi
- **fix(compression):** Lite truncation of a long multi-line tool result (OpenAI `tool` messages, Anthropic `tool_result` blocks and Responses text parts) now keeps the head and the tail with a `... [N lines elided] ...` marker instead of only the head, so line-addressed output such as numbered file reads and `rg` hits keeps its last lines; the tail stays inside the length cap and falls back to the head-only cut when even one tail line does not fit ([#18138](https://github.com/cheaper-inference/OmniRoute/pull/18138)) — thanks @DarknessKiller
- **fix(antigravity):** a request Cloud Code refuses with a hintless `429 Resource has been exhausted (e.g. check quota)` no longer turns into dozens of upstream calls. The refusal was multiplied by three layers — the executor's 4 tries per host on every host, the chat handler's cooldown-aware retry restarting the whole request (and re-running the quota preflight each time), and the client re-sending the body. Now the executor spends a per-request budget of upstream 429s across all hosts (`ANTIGRAVITY_MAX_429_ATTEMPTS`, default 2, 1–12) and returns the 429, the chat handler does not restart an `agy`/`antigravity` request after it, and an identical re-send of a body that spent the budget is answered locally (429 + `Retry-After`, zero upstream calls) for the upstream hint, else 2 min doubling to 30 min. The local answer is a verdict on that request, so it does not cool the connection down, mark it quota-exhausted, or trip the provider breaker (#18140). — thanks @Witroch4
- **fix(build):** production webpack builds no longer write the webpack persistent cache, which added about 2.5 GB of peak build memory at a 6144 MB heap cap; set `OMNIROUTE_WEBPACK_BUILD_CACHE=1` (or `--build-arg OMNIROUTE_WEBPACK_BUILD_CACHE=1` for Docker) to keep it for faster repeat builds ([#18141](https://github.com/diegosouzapw/OmniRoute/pull/18141)) — thanks @HouMinXi
- **fix(sse):** Gemini tool schemas no longer put `items` on a non-array node: a type union carrying `items` flattens to `array` (or `object` with `properties`) and an untyped node with `items` is typed `array`, avoiding the Gemini 400 `items: field predicate failed` (#18146 — thanks @rahmaangriawan).
- **fix(sse):** Passthrough streams now give a tool-call slot opener a string `id` when the upstream omits it, so strict OpenAI-compatible clients no longer abort with "Expected 'id' to be a string." (#18147 — thanks @fathonix).
- **fix(sse):** Gemini image models (Antigravity and AI Studio) now answer 400 for a reference image they cannot inline, such as an http(s) URL, instead of silently generating from the text prompt alone; AI Studio also forwards inline data-URI references (#18148 — thanks @shatinz).
- **fix(sse):** Claude clients now get Anthropic error types (`api_error`, `rate_limit_error`, …) on translated stream error frames instead of the OpenAI-only `server_error` (#18150 — thanks @alican532).
- **fix(sse):** CodeBuddy Intl 400 "request illegal" WAF blocks now back the connection off exponentially instead of being retried at cooldown 0 (#18151 — thanks @azhnan-dev).
- **fix(quota):** recognize international Kimi Coding usage endpoints (`api.kimi.ai/coding/v1`) under Moonshot connections (#18180). ([#18224](https://github.com/cheaper-inference/OmniRoute/pull/18224)) — thanks @NabiBukhsh-AI
- **fix(api):** List eligible provider-wildcard combos in `/v1/models` with metadata from their current model pool, without changing saved wildcard definitions or access rules ([#18182](https://github.com/cheaper-inference/OmniRoute/pull/18182)) — thanks @trespassmk
- **fix(build):** webpack builds bundle the locale catalogs (`src/i18n/messages/*.json`) as minified text that `src/i18n/request.ts` parses once per locale, instead of as `json` modules. Measured with the persistent webpack cache off (as #18141 sets it), the server compile holds about 1 GB less heap after code generation, and a clean build completes under a 4096 MB heap cap that ran out of heap before; the default caps (6144 MB in Docker, 8192 MB elsewhere) are unchanged ([#18183](https://github.com/diegosouzapw/OmniRoute/pull/18183)) — thanks @HouMinXi
- **fix(build):** Select webpack automatically for production builds with at most 16 GiB of detected process memory (or unknown limits), including container-constrained hosts, to avoid Turbopack OOM. Preserve explicit bundler overrides and warn when low-memory Turbopack is forced (#18184 - thanks @fabioKoruja00). ([#18242](https://github.com/cheaper-inference/OmniRoute/pull/18242))
- **fix(sse):** Enforce stream chunk log byte limits using UTF-8, preserving complete Unicode code points and bounding capture independently at all three pipeline stages ([#18187](https://github.com/cheaper-inference/OmniRoute/pull/18187)) — thanks @AiCaptain357
- **fix(api):** Bound model catalog response and last-good caches by entry count and retained string bytes, preventing distinct pagination cursors from accumulating catalog responses ([#18188](https://github.com/cheaper-inference/OmniRoute/pull/18188)) — thanks @AiCaptain357
- **fix(translator):** OpenAI→Claude deferred-finish flush no longer replays the finish chunk's trailing text / tool-arg fragment, which produced invalid tool-input JSON ([#18193](https://github.com/cheaper-inference/OmniRoute/pull/18193)) — thanks @starxjys
- **fix(usage):** Release terminal pending requests even when completion bookkeeping or completed-detail retention throws, preserving concurrent requests and their counters ([#18217](https://github.com/cheaper-inference/OmniRoute/pull/18217), [@AiCaptain357](https://github.com/AiCaptain357)).
- **fix(codex):** classify explicit transient Codex capacity HTTP 429s separately from exhausted quota. Avoid virtual-scope cooldowns and shared limiter pauses, honor `disableCooling`, retain bounded exact-model backoff and account rotation, and preserve genuine quota/reset evidence (#18221 - thanks @fabioKoruja00). ([#18239](https://github.com/cheaper-inference/OmniRoute/pull/18239))
- **fix(combo):** run every combo health probe with its own deadline instead of skipping remaining models after 180 seconds of accumulated elapsed time. Preserve the existing per-target aborts, request cancellation, sequential order, and bounded auto-combo sampling (#18231 - thanks @fabioKoruja00). ([#18238](https://github.com/cheaper-inference/OmniRoute/pull/18238))
- **fix(sse):** the JSON-schema fallback for kilocode / `openai-compatible-*` and opencode-zen's DeepSeek models no longer writes its instruction into the caller's system content, so a 429 retry inside one request stops sending it twice; `response_format: { type: "text" }` and other non-JSON formats no longer get the JSON instruction (kilocode still drops the field) (#18241). — thanks @AndresDiagoM
- **fix(embeddings):** Preserve Gemini native `usageMetadata.promptTokenCount` in OpenAI-compatible `prompt_tokens` and `total_tokens` for single and batch embeddings, without double-counting batch vectors. Missing or invalid counts safely remain zero; embedding values and indexes are unchanged (#18244). ([#18246](https://github.com/cheaper-inference/OmniRoute/pull/18246)) — thanks @fabioKoruja00
- **fix(translator):** OpenAI explicit prompt caching (GPT-5.6+) now works on Chat Completions through `openai` and `openrouter`: `prompt_cache_breakpoint` is kept on content parts instead of being dropped by the #16384 field allowlist, so `prompt_cache_options: { mode: "explicit" }` caches again and 12h breakpoints are no longer billed as 30-minute writes ([#18255](https://github.com/cheaper-inference/OmniRoute/pull/18255)) — thanks @cris005
- **fix(sse):** keep the `thinking` config on tool-result turns (OpenAI `tool`, Gemini `function`); only an assistant/model prefill strips it ([#18262](https://github.com/diegosouzapw/OmniRoute/pull/18262)) — thanks @claytontavaresdan
- **fix(api):** decode zstd/gzip/br/deflate-compressed JSON request bodies once at chat admission (Codex request compression), with the expanded size held to the hard body limit ([#18264](https://github.com/diegosouzapw/OmniRoute/pull/18264)) — thanks @DaDecky
- **fix(deepseek):** native DeepSeek no longer returns HTTP 400 on structured-output requests: `json_schema` response_format is downgraded to `json_object` with the schema carried in the system prompt ([#18265](https://github.com/diegosouzapw/OmniRoute/pull/18265)) — thanks @yunaremaia / @claytontavaresdan
- **fix(sse):** strip invisible characters (zero-width space/joiners, word joiner, BOM, stray control characters) from outbound header values, so an API key pasted with one no longer breaks every request on that connection with a ByteString/invalid-header error; valid characters are never altered ([#18266](https://github.com/diegosouzapw/OmniRoute/pull/18266)) — thanks @claytontavaresdan
- **fix(sse):** the reasoning_content placeholder injector no longer adds a second reasoning carrier to assistant messages that already send the `reasoning` alias or an array-valued carrier, which serde-based upstreams rejected as a duplicate field ([#18273](https://github.com/diegosouzapw/OmniRoute/pull/18273)) — thanks @AppleVibe
- **fix(opencode):** publish the real ~330k context window for opencode-zen `deepseek-v4.1-flash-free` (and its legacy `deepseek-v4-flash-free` alias) over the synced 1M, so clients compact before the free backend rejects the prompt ([#18277](https://github.com/diegosouzapw/OmniRoute/pull/18277)) — thanks @claytontavaresdan
- **fix(search):** Exa results now carry every highlight in `provider_raw.highlights`, next to the `highlight_scores` they belong to; before, only the first highlight survived (as the `snippet`) ([#18282](https://github.com/diegosouzapw/OmniRoute/pull/18282), #18109) — thanks @floydous / @yourspraveen
- **fix(antigravity):** accept the bare `gemini-4-argon` id (defaults to the medium tier) and list Gemini 4 Argon in the Vertex catalog ([#18285](https://github.com/cheaper-inference/OmniRoute/pull/18285)) — thanks @apr3ndi5 / @AppleVibe
- **fix(sse):** Gemini tool schemas now type empty or typeless array `items` (`items: {}` or items emptied by keyword stripping) at any depth instead of sending them typeless ([#18289](https://github.com/cheaper-inference/OmniRoute/pull/18289)) — thanks @TomSeestern
- **fix(api):** provider model discovery now reports an unreachable upstream (connection refused, DNS or TLS failure) with no cached or local catalog as `502 UPSTREAM_UNREACHABLE` instead of a generic 500, and the timeout (504) response no longer echoes the request URL's `?key=` credential ([#18294](https://github.com/cheaper-inference/OmniRoute/pull/18294)) — thanks @alfep
- **fix(db):** deleting a combo now revokes its name from every API key's allowed combos, so a combo recreated under the same name is no longer granted to keys that had the old one ([#18297](https://github.com/cheaper-inference/OmniRoute/pull/18297)) — thanks @Jordannst
- **fix(translator):** Claude/Gemini/Antigravity → OpenAI now keep an explicit client `max_tokens` on tool-calling requests instead of raising it to the 32000 tool floor, the same way OpenAI → Claude already does. A client that sends a tiny cap with tools now gets exactly that cap ([#18300](https://github.com/cheaper-inference/OmniRoute/pull/18300)) — thanks @claytontavaresdan
- **fix(providers):** A custom Codex model that no synced account inventory lists (for example one released after the last sync) no longer fails with `provider_not_configured` while Codex accounts are active. Account inventories now filter Codex accounts only when at least one inventory advertises the requested model, so a model listed by one account still routes only to that account and image models stay exempt ([#18311](https://github.com/diegosouzapw/OmniRoute/issues/18311)) ([#18367](https://github.com/cheaper-inference/OmniRoute/pull/18367)) — thanks @HouMinXi
- **fix(translator):** keep Chat Completions custom tools (`type: "custom"`) when routing to Claude instead of silently dropping them ([#18314](https://github.com/cheaper-inference/OmniRoute/pull/18314)) — thanks @gebclogic
- **fix(api):** `OMNIROUTE_NO_UPDATE_NOTIFIER` now also silences the dashboard "update available" notice: `GET /api/system/version` skips the npm, GitHub and news lookups and reports no update ([#18316](https://github.com/cheaper-inference/OmniRoute/pull/18316)) — thanks @ifauzeee
- **fix(auth):** dashboard logout only reports success after the session revocation is saved, returns a retryable 503 instead of a false sign-out, and no longer loses a revocation when two sign-outs race ([#18323](https://github.com/cheaper-inference/OmniRoute/pull/18323)) — thanks @MiRHaDi
- **fix(cursor):** fail closed when a request pinned to a proxy cannot resolve that proxy (it previously fell back to a direct connection and egressed on the host IP — a broken pinned proxy config now surfaces as a connection error), bound the proxy CONNECT response header at 64 KiB, and require ALPN h2 on the tunnelled TLS session ([#18324](https://github.com/cheaper-inference/OmniRoute/pull/18324)) — thanks @z3ncv
- **fix(sse):** distinguish generic buffered error frames from identified Responses failures while preserving tool and image output ([#18370](https://github.com/cheaper-inference/OmniRoute/pull/18370), split from [#17550](https://github.com/cheaper-inference/OmniRoute/pull/17550)) — thanks @KiaroSama
- **fix(sse):** the GLM Anthropic transport (`glm-5.2-high` / `glm-5.2-max` effort tiers, and any GLM request routed to `/api/anthropic`) sent `thinking: { type: "enabled" }` without `budget_tokens`, and Z.ai rejected it with HTTP 400 `missing field budget_tokens`. Enabled thinking now always carries a budget. A client-supplied budget is kept; a missing one comes from the tier (high 24576, max 131072, medium 10240 for other models) and is clamped to `max_tokens - 1024` ([#18378](https://github.com/cheaper-inference/OmniRoute/pull/18378)) — thanks @mj10612 / @AppleVibe
- **fix(sse):** emit Chat Completions startup and periodic heartbeats without application choices, preserving LangChain tool-call aggregation and the upstream message identity while keeping JSON-compatible keepalives ([#18410](https://github.com/cheaper-inference/OmniRoute/issues/18410)). ([#18468](https://github.com/cheaper-inference/OmniRoute/pull/18468))
- **fix(network):** refuse the reserved 240.0.0.0/4 range and the 255.255.255.255 broadcast in the SSRF guard, in IPv4 and IPv6-embedded forms and as DNS answers; 198.18.0.0/15 stays allowed for fake-IP proxy setups ([#18415](https://github.com/cheaper-inference/OmniRoute/pull/18415))
- **fix(sse):** self-built SSE responses that bypass the central header path (early-keepalive slow path, synthetic bypass stream, MCP SSE transport) now send `X-Accel-Buffering: no` so buffering reverse proxies stop sitting on the stream ([#18446](https://github.com/cheaper-inference/OmniRoute/pull/18446)) — thanks @jjxyxsjr
- Continue automatic web-fetch fallback after Jina Reader returns HTTP402 for insufficient balance; preserve explicit-provider and authorization-error behavior. ([#18455](https://github.com/cheaper-inference/OmniRoute/pull/18455)) — thanks @Grapescreations
- **fix(sse):** report a buffered upstream stream that closes before its terminal signal as truncated (`finish_reason: "length"`, Claude `max_tokens`, Responses `incomplete`) instead of a clean `stop` on a partial answer for non-streaming clients ([#18460](https://github.com/cheaper-inference/OmniRoute/pull/18460), refs [#18432](https://github.com/cheaper-inference/OmniRoute/issues/18432))
- **fix(sse):** a buffered upstream stream cut while a tool call is still being emitted no longer reaches non-streaming clients as `finish_reason: "tool_calls"` with half-written JSON arguments — the incomplete calls are withheld and the turn reports cut short (`length` / Claude `max_tokens` / Responses `incomplete`) ([#18482](https://github.com/cheaper-inference/OmniRoute/pull/18482), refs [#18460](https://github.com/cheaper-inference/OmniRoute/pull/18460))
- **fix(combo):** a non-streaming combo attempt whose buffered upstream stream was cut before its terminal signal now fails over to the next target instead of returning the partial `length` answer; a genuine upstream `length` is still a success, the cut trips neither the provider circuit breaker nor a model lockout, and when every target is cut the last partial result is returned as before ([#18490](https://github.com/cheaper-inference/OmniRoute/pull/18490), refs [#18460](https://github.com/cheaper-inference/OmniRoute/pull/18460))
- **fix(providers):** Anthropic API-key connections now get the same automatic prompt-cache breakpoints as the Claude OAuth provider; OpenAI-format clients were billed full uncached input on every turn ([#18532](https://github.com/cheaper-inference/OmniRoute/pull/18532), closes [#18531](https://github.com/cheaper-inference/OmniRoute/issues/18531)) — thanks @razakiau
- **fix(ws):** with `REQUIRE_API_KEY=true`, the `/v1/ws` bridge upgraded a socket that carried no API key, or only an invalid `?api_key=`, and only the requests sent over it got `401`. The handshake now needs a valid API key or dashboard session, as on `/v1/responses`, and refuses before upgrading: `401 ws_auth_required` without a credential, `403 ws_auth_invalid` for an invalid query key. The dashboard's plain handshake `GET` is unchanged. ([#18540](https://github.com/cheaper-inference/OmniRoute/pull/18540)) — thanks @HouMinXi
- **fix(sse):** the claude thinking constraint no longer re-injects `temperature` after an operator param filter or built-in strip rule removed it (and no longer invents one when the client sent no sampling param at all) — temperature-retired models stopped 400ing with "temperature is deprecated"; a lone `top_p` under thinking still converts to the pinned branch as before (#18636) ([#18639](https://github.com/cheaper-inference/OmniRoute/pull/18639)) — thanks @serenity-X
- **fix(sse):** a connection whose account runs out of credit is now parked as `credits_exhausted` even when the failure happens inside combo fallback — previously the combo path dropped the terminal status and the dead account kept receiving traffic (#18633) ([#18649](https://github.com/cheaper-inference/OmniRoute/pull/18649)) — thanks @serenity-X
- **fix(usage):** API-key spend recorded in the last flush interval is no longer lost on a graceful restart; the shutdown handler flushed a separate copy of the spend batch writer, so pending cost entries (up to 60 s by default) were dropped ([#18680](https://github.com/cheaper-inference/OmniRoute/pull/18680), closes [#18678](https://github.com/cheaper-inference/OmniRoute/issues/18678)) — thanks @yourspraveen
- **fix(sse):** Leonardo and Ideogram image models are treated as image-only: the dashboard model Test skips them instead of sending a chat request (Leonardo answered 404 "Endpoint not found"), and `/v1/chat/completions` rejects them with a 400 instead of forwarding them upstream and locking the model out ([#18683](https://github.com/cheaper-inference/OmniRoute/pull/18683), closes [#18682](https://github.com/cheaper-inference/OmniRoute/issues/18682)) — thanks @yourspraveen
- **fix(build):** npm 12 install-script approvals use name-only `allowScripts` entries, so native dependency install scripts stay approved across patch bumps; `onnxruntime-node` is explicitly denied under npm 12, while the `onnxruntime-node-install=skip` npmrc key stays as the npm 11.15 project-level guard ([#18690](https://github.com/cheaper-inference/OmniRoute/pull/18690)) — thanks @maxmad64bis
- **fix(docs):** `/docs` pages now show the translated mirror for the selected locale. The locale cookie was read without `await`, so every request silently got English; the translated view also gets its own table of contents, page title, working images and base-path links, and reaches doc names with underscores. npm and Docker installs still ship no mirrors and keep serving English ([#18768](https://github.com/cheaper-inference/OmniRoute/pull/18768))
- **fix(mcp):** Client-provided _meta scopes are now ignored for local (stdio) MCP callers when scope enforcement is on, so the OMNIROUTE_MCP_SCOPES env key stays the only local grant ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/PRNUM)) ([#17112](https://github.com/cheaper-inference/OmniRoute/pull/17112)) ([#17112](https://github.com/diegosouzapw/OmniRoute/pull/17112)) — thanks @maxmad64bis
- Restore shared dashboard type contracts for no-auth provider statistics, React JSX, activity totals, and optional donut legend callbacks; reject malformed OAuth authorization URLs before opening a browser window. ([#18457](https://github.com/cheaper-inference/OmniRoute/pull/18457))
- **fix(usage):** include the shared Kimi Coding monthly allowance in quota results while preserving existing weekly and five-hour windows. ([#18234](https://github.com/cheaper-inference/OmniRoute/pull/18234)) — thanks @AiCaptain357
- **fix(sse):** Cancel TLS response bodies when the caller aborts after headers and release their session leases without disturbing active responses. ([#18228](https://github.com/cheaper-inference/OmniRoute/pull/18228)) — thanks @AiCaptain357
- **fix(ui):** Prevent intermittent Vitest teardown errors by unmounting Studio React roots, stabilizing the Provider Stats translation mock, and cancelling OmniGlyph saved-status timers when saving again or unmounting. Late OmniGlyph save completions no longer update an unmounted page. ([#18525](https://github.com/cheaper-inference/OmniRoute/pull/18525)) — thanks @fabioKoruja00
- **fix(dashboard):** codemod leftover detection across files, scanner sees <Icon name> (KEA-7886, trilha B) (direct commit `9381f94d8b`)
- **fix(build):** tsconfig keeps the vendored kit in scope so tsx compiles it with react-jsx (KEA-7886, F1) (direct commit `ebf06ee41b`)
- **fix(i18n):** Vietnamese carries the five kit-shell keys its parity test pins (KEA-7886, F1) (direct commit `92fd5c1cdf`)
- **fix(dashboard):** single sidebar trigger in the kit shell; Header description keys match the live items (KEA-7886, trilha G) (direct commit `e5728a0325`)
- **fix(dashboard):** sticky sidebar search covers the scroll strip; skip-link check runs on mobile; budget notes (KEA-7886, trilha F) (direct commit `fd23bca535`)
- **fix(dashboard):** Select submits its own hidden input and merges aria-describedby; empty labels render nothing (KEA-7886, trilha C) (direct commit `89c7f7f232`)
- **fix(dashboard):** Card adapter keeps the legacy padding and layout contract on the root; disabled bare Toggle dims (KEA-7886, trilha C) (direct commit `c313736cce`)
- **fix(dashboard):** RTL rail keeps right-0 (globals.css mirrors it to the content edge); RTL e2e waits for the kit transition (KEA-7886, trilha F) (direct commit `f9e24e2e42`)
- **fix(dashboard):** HeaderLegacy description keys name the live proxy item, so no TS2353/TS2561 needs a baseline (KEA-7886, trilha F) (direct commit `ba43b51c2f`)
- **fix(dashboard):** Select adapter types the kit-root prop spread (KEA-7886, trilha C) (direct commit `d29f5dc11c`)
- **fix(dashboard):** SegmentedControl size overrides the kit minimum height (KEA-7886, trilha D) (direct commit `a40f66a37d`)
- **fix(dashboard):** HeaderLegacy drops the dead settings key instead of baselining its TS2353 (KEA-7886, trilha F) (direct commit `63ff19b471`)
- **fix(dashboard):** kit sidebar subpath imports, RTL sidebar keeps the inline-end side, skip link reachable on mobile, search on top of SidebarContent, footer labels do not truncate (KEA-7886, trilha F) (direct commit `8630295533`)
- **fix(dashboard):** first-run icon ink and basePath scan cover template literals (KEA-7886, trilha E) (direct commit `160528e629`)
- **fix(dashboard):** DataTable keeps auto width; EmptyState emoji fits the tile; view_column mapped (KEA-7886, trilha D) (direct commit `8bb7a526b6`)
- **fix(dashboard):** SegmentedControl drops aria-controls, maps size and keeps the phone layout; closed CollapsibleSection is inert; Collapsible title truncates (KEA-7886, trilha D) (direct commit `50d1893dea`)
- **fix(dashboard):** header height absorbs the safe-area inset; palette field styles the input shell; language selector name follows label-in-name; no banner inside main (KEA-7886, trilha G) (direct commit `d7d50b5a70`)
- **perf(dashboard):** shell components import kit subpaths (KEA-7886, trilha G) (direct commit `3f24b95169`)
- **fix(dashboard):** C1 follow-ups — Button variant fallback, alert role query, icon-size tests (KEA-7886, trilha C) (direct commit `7c4fd35758`)
- **fix(dashboard):** OmniRouteLogo honours size against the kit 2em rule; muted decorative icons (KEA-7886, trilha E) (direct commit `b3716ae727`)
- **fix(dashboard):** command palette search hides the native cancel button beside its own clear button (KEA-7886, trilha G) (direct commit `0c3d259587`)
- **fix(dashboard):** kit rail stays inside the sidebar on the content edge, icon rail paints only glyphs, lockup does not wrap (KEA-7886, trilha F) (direct commit `926a089220`)
- **fix(dashboard):** Tooltip wraps disabled triggers; dialog layout reconciled with the kit (KEA-7886, trilha D) (direct commit `9452dcd33f`)
- **fix(dashboard):** Modal ignores outside-clicks on toasts; toaster root stays live under a dialog (KEA-7886, trilha D) (direct commit `840c88e3a1`)
- **perf(dashboard):** D1 adapters import kit subpaths instead of the barrel (KEA-7886, trilha D) (direct commit `7e6ae03d11`)
- **perf(dashboard):** logo wrappers import the kit brand module, not the barrel (KEA-7886, trilha E) (direct commit `9b9ffdbe46`)
- **fix(dashboard):** Modal adapter drops the duplicated sr-only description, realign portal queries (KEA-7886, trilha D) (direct commit `831ab9c5f2`)
- **fix(dashboard):** Input adapter keeps role=alert on the error message (KEA-7886, trilha C) (direct commit `576ebc730c`)
- **fix(dashboard):** Input adapter keeps the accessible name free of the required marker (KEA-7886, trilha C) (direct commit `aa43e5638c`)
- **fix(dashboard):** one skip link, first in tab order on dashboard routes (KEA-7886, F4) (direct commit `9cd3823096`)
- **fix(dashboard):** primary Badge tone readable in dark (KEA-7886, F4) (direct commit `2252bc3534`)
- **fix(dashboard):** timeline header wraps at 390 px instead of clipping its toolbar (KEA-7886, F4) (direct commit `01199bf3c1`)
- **fix(dashboard):** locked profile badges keep AA text — dashed tile, dimmed glyph, lock mark (KEA-7886, F4) (direct commit `0435db2fa4`)
- **fix(dashboard):** drop the shell title from the DOM next to a PageHeader instead of CSS-hiding it (KEA-7886, F4) (direct commit `3893075f76`)
- **fix(dashboard):** PageHeader on memory, mcp, a2a, cloud-agents, agent-skills, omni-skills (KEA-7886, F4) (direct commit `7b43b3a011`)
- **fix(dashboard):** endpoint cloud destination no longer splits words at 390 px (KEA-7886, F4) (direct commit `05f6bcff63`)
- **fix(dashboard):** one page title — the shell title hides when the page has a PageHeader (KEA-7886, F4) (direct commit `6455f498c8`)
- **fix(dashboard):** onboarding stepper fits 390 px, step title is the page h1 (KEA-7886, F4) (direct commit `8bcce6e96a`)
- **fix(dashboard):** SSR-consistent first render in useDisplayBaseUrl — React #418 on /relay (KEA-7886, F4) (direct commit `520ab9cf61`)
- **fix(dashboard):** return focus to the opener when a dialog field has autoFocus (KEA-7886, F4) (direct commit `2c861d06e9`)
- **fix(dashboard):** readable primary Badge tone in dark (KEA-7886, F4) (direct commit `8487586db6`)
- **fix(dashboard):** provider category counts at full-opacity muted ink (KEA-7886, F4) (direct commit `03a8f515fc`)
- **fix(dashboard):** lote 6 — iconMap imports Flame once after the lote 8 back-merge (KEA-7886) (direct commit `213a176ebc`)
- **fix(dashboard):** lote 5b — review fix round 1 (KEA-7886, lote 5b) (direct commit `c35fe9ee4c`)
- **fix(dashboard):** lote 8 — profile badge icons on Lucide, Faro chat row wraps at 390, iconMap imports once (KEA-7886, lote 8) (direct commit `3ff76ed7e3`)
- **fix(dashboard):** lote 6 — review round 1 (timeline ink, translator select names, adapters) (KEA-7886) (direct commit `fd433c41b6`)
- **fix(dashboard):** vi — common.pagination and proxyRegistry.moreActions (KEA-7886, lote 5b) (direct commit `82bba065ab`)
- **fix(dashboard):** logger and timeline toolbar fields keep their intrinsic width (KEA-7886, lote 6) (direct commit `e32288b302`)
- **fix(dashboard):** lote 5a — field errors on the field, named inputs and groups, slider presets, pin glyph (KEA-7886) (direct commit `ca96d496fd`)
- **fix(dashboard):** lote 7 — cursor auth modal test, card names and keyboard, risk modal overlay, sheet focus return (KEA-7886, lote 7) (direct commit `ae6f72909a`)
- **fix(dashboard):** lote 9 — provider-stats keeps its header on error, collapsibles on the adapter, empty states unframed, tab panels (KEA-7886, lote 9) (direct commit `28efe3f5a4`)
- **fix(dashboard):** lote 10 — TokenHealthBadge popover keeps focus, non-dismissable Node alerts, miniapp label (KEA-7886, lote 10) (direct commit `1e3a300e1d`)
- **fix(dashboard):** lote 8 — integration card ink, media endpoint wrap, webhook toggle name (KEA-7886, lote 8) (direct commit `aed3a30960`)
- **fix(dashboard):** settings — muted hints and disabled values keep AA contrast (axe, KEA-7886, lote 5a) (direct commit `c6e3bc3cb1`)
- **fix(dashboard):** lote 8 — compare provider picker uses the kit Checkbox (card prop typed) (KEA-7886, lote 8) (direct commit `aa1657ae24`)
- **fix(dashboard):** iconMap — contrast (system theme) maps to Contrast (KEA-7886, lote 5a) (direct commit `210d5260a1`)
- **fix(dashboard):** lote 10 — axe contrast on legal links, login footer and the PATCH badge (KEA-7886, lote 10) (direct commit `2a3656205d`)
- **fix(dashboard):** drop Lucide imports duplicated by the iconMap union merge (KEA-7886, lote 6) (direct commit `00f7175ddd`)
- **fix(dashboard):** settings/routing — the default strategy is a kit Select, not a 20-tab strip (KEA-7886, lote 5a) (direct commit `66f6acbe5c`)
- **fix(dashboard):** lote 8 — service-kind tabs label key in en, pt-BR and vi (KEA-7886, lote 8) (direct commit `70729b6d4d`)
- **fix(dashboard):** map hourglass_empty for the cooldowns loading state (KEA-7886, lote 9) (direct commit `a01d7750a7`)
- **fix(dashboard):** lote 7 — CLI tool-card rows shrink at phone width, detail header keeps only the back link (KEA-7886, lote 7) (direct commit `09fc4ca369`)
- **fix(dashboard):** lote 5b smoke — PII flag chips wrap as text, description-mode buttons stay on one line, secret preview names the token, free-pool filters on one row (KEA-7886, lote 5b) (direct commit `28fa9e06ff`)
- **fix(dashboard):** settings — fixed-width fields size their Field, units stay in the row (KEA-7886, lote 5a) (direct commit `8d8327871c`)
- **fix(dashboard):** landing lockup wordmark takes white ink on the dark shell (KEA-7886, lote 10) (direct commit `3707189ad2`)
- **fix(dashboard):** keep the session recorder buttons' aria-labels (KEA-7886, lote 9) (direct commit `23b5e2430c`)
- **fix(dashboard):** lote 8 — kit Alert dismiss button translated and clearing its message (KEA-7886, lote 8) (direct commit `6279908c80`)
- **fix(dashboard):** Checkbox adapter declares and forwards indeterminate (KEA-7886) (direct commit `c6366bc63e`)
- **fix(i18n):** lote 3 keys in vi (KEA-7886, lote 3) (direct commit `4c750993cc`)
- **fix(dashboard):** settings/security heading ink uses the defined text-main token (KEA-7886, lote 5b) (direct commit `b9d4f934ee`)
- **fix(dashboard):** lote 4 — context-length field errors through the adapter, control-center heading and labels (KEA-7886, lote 4) (direct commit `721ddfd729`)
- **fix(dashboard):** lote 3 — icon map duplicate import, category picker on CheckboxGroup, tokens shell, table scrollbar labels (KEA-7886, lote 3) (direct commit `93ff93bf20`)
- **fix(dashboard):** lote 2b — wizard type-card icons at full size, step/avatar glyphs mapped (KEA-7886, lote 2b) (direct commit `0d5a8b157f`)
- **fix(dashboard):** lote 1 — health pool bars are decorative, numbers carry the value (KEA-7886, lote 1) (direct commit `87422fa6e3`)
- **fix(dashboard):** lote 2b — select-all indeterminate state, table container, surface tokens, icon button names (KEA-7886, lote 2b) (direct commit `1ec7c8a30a`)
- **fix(dashboard):** lote 1 — eval suite rows wrap their actions at phone width (KEA-7886) (direct commit `e4f6d7b752`)
- **fix(dashboard):** lote 1 — key filter as a disclosure, visible combo tracks, health header on top with kit Progress (KEA-7886) (direct commit `d1d71c339c`)
- **fix(dev):** icon codemod keeps the use-client directive first and reports multi-line dynamic spans (KEA-7886) (direct commit `509ba40edb`)
- **fix(dashboard):** providers modals — decorative import progress, compact priority row (KEA-7886, lote 2a) (direct commit `b300cb2ff5`)
- **fix(dashboard):** /dashboard/usage free-budget sort control shrinks at phone width (KEA-7886, lote 1) (direct commit `5f1d302921`)
- **fix(dashboard):** analytics group — usage analytics toolbar wraps at phone width (KEA-7886, lote 1) (direct commit `3a27b981d3`)
- **fix(dashboard):** analytics tabs keep "use client" as the module directive (KEA-7886, lote 1) (direct commit `10fc9eec4e`)
- **fix(dashboard):** analytics group — sort direction typed for the kit SortableTableHead (KEA-7886, lote 1) (direct commit `39e9123b8d`)
- **fix(dashboard):** align the release's max-output-tokens field with the UI kit (KEA-7886) (direct commit `88f46fcc29`)
- **fix(dashboard):** F4 sweep — review round 1 (endpoint fallback, header scope, exception marker, tests) (KEA-7886, F4) (direct commit `81e7927707`)
- **fix(dashboard):** typed node interpolation for the MCP/A2A {code} arguments (KEA-7886, F4) (direct commit `7b00c96bb5`)
- **fix(dashboard):** F4 — distinct missing-icon glyph, CDN guard, coverage hint (direct commit `e0d613861e`)
- **fix(dashboard):** checkpoint axe findings — console timestamps, engine rows, caveman preview, acp link (KEA-7886, F4) (direct commit `c64e1197fb`)
- **fix(dashboard):** MCP/A2A step hints pass nodes to their ICU {code} arguments (KEA-7886, F4) (direct commit `acd8f10f24`)
- **fix(dashboard):** render the profile title through the kit PageHeader (KEA-7886 P12) (direct commit `dda3e0166f`)
- **fix(pwa):** use the kit background for theme-color and the web manifest (KEA-7886 P9) (direct commit `fff2cea066`)
- **fix(dashboard):** give bg-alt, muted and surface-secondary their @theme entries (KEA-7886 P4) (direct commit `1c71ab9b8f`)
- **fix(dashboard):** AA note on the VS Code banner, /dashboard axe baseline 1 -> 0 (KEA-7886 P7) (direct commit `ce7e24812c`)
- **fix(dashboard):** AA contrast for the partner banners on /home (KEA-7886 P7) (direct commit `8afa633e35`)
- **fix(dashboard):** stale-model badge and preferred-connections controls on the kit adapters (KEA-7886 guards) (direct commit `8b02bfb7bd`)
- **fix(api):** explain the Windows EBUSY/EPERM failure of the in-app npm update ([#17335](https://github.com/cheaper-inference/OmniRoute/pull/17335))
- **fix(opencode):** scope a retired-model 410 to the model, not the connection ([#17827](https://github.com/cheaper-inference/OmniRoute/pull/17827)) — thanks @adrian-cancio
- **fix(ci):** accept Dependabot's support@github.com sign-off in check:ai-attribution ([#17876](https://github.com/cheaper-inference/OmniRoute/pull/17876))
- **fix(call-logs):** let an idle artifact worker stop holding the process open ([#18056](https://github.com/cheaper-inference/OmniRoute/pull/18056)) — thanks @HouMinXi
- **fix(docker):** overlay npm's bundled ip-address with 10.7.4 (GHSA-rxp7-6hxx-x8q5) ([#18085](https://github.com/cheaper-inference/OmniRoute/pull/18085))
- **fix(db):** don't skip half-applied proxy timing migration 194 ([#18096](https://github.com/cheaper-inference/OmniRoute/pull/18096)) — thanks @Bl0ck154
- **fix(firefly):** avoid false stale detection for non-underscore Forter timestamps ([#18099](https://github.com/cheaper-inference/OmniRoute/pull/18099)) — thanks @AbdullahPesteli
- **fix(gemini):** preserve cached and reasoning tokens in buffered SSE usage metadata ([#18100](https://github.com/cheaper-inference/OmniRoute/pull/18100)) — thanks @Bl0ck154
- **fix(compression):** report RTK's real truncation count and cut on line boundaries ([#18139](https://github.com/cheaper-inference/OmniRoute/pull/18139)) — thanks @DarknessKiller
- **fix(quality):** record the four UI-kit test deletions/realignments the release-green test-masking sweep flags against main ([#18149](https://github.com/cheaper-inference/OmniRoute/pull/18149))
- **fix(release):** drain the deterministic reds of release/v3.8.53 ([#18170](https://github.com/cheaper-inference/OmniRoute/pull/18170))
- **fix(api):** restore the API route typecheck on release/v3.8.53 ([#18213](https://github.com/cheaper-inference/OmniRoute/pull/18213))
- **fix(sse):** inject the remote media downloader into URL transcription ([#18214](https://github.com/cheaper-inference/OmniRoute/pull/18214))
- **fix(changelog):** format security fragment as a Markdown bullet ([#18259](https://github.com/cheaper-inference/OmniRoute/pull/18259)) — thanks @Bl0ck154
- **fix(docs):** refresh the live counts and the design-system palette note on release/v3.8.53 ([#18339](https://github.com/cheaper-inference/OmniRoute/pull/18339))
- **fix(i18n):** use the canonical zh provider term in the providerStats and discovery keys ([#18372](https://github.com/cheaper-inference/OmniRoute/pull/18372))
- **fix(docs):** keep the routing-strategy identifiers verbatim in the Bosnian AGENTS.md mirror ([#18382](https://github.com/cheaper-inference/OmniRoute/pull/18382))
- **fix(i18n):** fall back to the next model and re-split when a chunk fails validation ([#18422](https://github.com/cheaper-inference/OmniRoute/pull/18422))
- **fix(release):** drain the deterministic unit and vitest reds of release/v3.8.53 ([#18426](https://github.com/cheaper-inference/OmniRoute/pull/18426))
- **fix(release):** bring the open-sse → src boundary back to 774 on release/v3.8.53 ([#18467](https://github.com/cheaper-inference/OmniRoute/pull/18467))
- **fix(i18n):** open the LLM circuit after three consecutive failures, not only on quota ([#18474](https://github.com/cheaper-inference/OmniRoute/pull/18474))
- **fix(docs):** sync the migration and provider counts to the live tree (base-red #17219) ([#18476](https://github.com/cheaper-inference/OmniRoute/pull/18476))
- **fix(dashboard):** monospace code text and a non-Latin script fallback after the kit 0.0.66 bump ([#18477](https://github.com/cheaper-inference/OmniRoute/pull/18477))
- **fix(release):** list the three covering tests in stryker tap.testFiles ([#18485](https://github.com/cheaper-inference/OmniRoute/pull/18485))
- **fix(i18n):** protect inline HTML in the Google engine and guard HTML structure in the output check ([#18486](https://github.com/cheaper-inference/OmniRoute/pull/18486))
- **fix(dashboard):** bring the team costs, combo step test and catalog toggle onto the kit components ([#18487](https://github.com/cheaper-inference/OmniRoute/pull/18487))
- **fix(release):** clear the 10-28 typecheck, boundary, stryker, route-map and i18n reds on release/v3.8.53 ([#18515](https://github.com/cheaper-inference/OmniRoute/pull/18515))
- **fix(i18n):** escape the Maltese apostrophe in providers.usedInComboTooltip (base-red) ([#18545](https://github.com/cheaper-inference/OmniRoute/pull/18545))
- **fix(antigravity):** restore saved Argon cooldowns after restart ([#18663](https://github.com/cheaper-inference/OmniRoute/pull/18663)) — thanks @HouMinXi
- **fix(cloud-agent):** keep Jules creation timestamps consistent ([#18681](https://github.com/cheaper-inference/OmniRoute/pull/18681)) — thanks @HouMinXi
- **fix(ci):** run main-green's suites on hosted runners ([#18776](https://github.com/cheaper-inference/OmniRoute/pull/18776))
- **fix(ci):** raise the Release-Green package lane ceiling to 60 min ([#18794](https://github.com/cheaper-inference/OmniRoute/pull/18794))
- **fix(dashboard):** the browser bar / installed-PWA title bar (`<meta name="theme-color">`) now follows the Classic palette when it is picked in Settings → Appearance, in light and dark, from the first paint and on every live switch; the server default and the web manifest stay Editions, since the manifest is fetched once per install without the preference ([#19011](https://github.com/cheaper-inference/OmniRoute/pull/19011))
- **fix(combo):** Priority and round-robin fallback waits now stop immediately when the client has already disconnected and release abort listeners when the wait completes. ([#18569](https://github.com/cheaper-inference/OmniRoute/pull/18569)) — thanks @AiCaptain357
- **fix(sse):** Combo set retries now return 499 immediately when the request is already cancelled and release abort listeners when retry delays expire. Zero-delay retries still yield before dispatching again. ([#18549](https://github.com/cheaper-inference/OmniRoute/pull/18549)) — thanks @AiCaptain357
- **fix(providers):** Preserve the original caller's local CLI restriction during Cursor model sync and carry trusted locality into the in-process fallback. ([#18629](https://github.com/cheaper-inference/OmniRoute/pull/18629)) — thanks @AiCaptain357
- **fix(dashboard):** Show the Kimi provider name in quota cards, overview groups and the provider filter instead of its internal moonshot identifier. ([#18571](https://github.com/cheaper-inference/OmniRoute/pull/18571)) — thanks @AiCaptain357
- Deduplicate completed previews against their exact saved rows in the filtered result; keep genuine retries separate and cached details accessible through the saved ID while the linked preview remains available. ([#18491](https://github.com/cheaper-inference/OmniRoute/pull/18491)) — thanks @AiCaptain357
- **fix(logging):** run source call-log artifact workers with native Node.js TypeScript support so idle shutdown and forced termination do not leave esbuild zombies. ([#18511](https://github.com/cheaper-inference/OmniRoute/pull/18511)) — thanks @AiCaptain357
- **fix(providers):** Check Cloudflare Workers AI credentials with account-scoped model metadata instead of inference; support explicitly configured Global API Key headers and keep ambiguous 403/429 probes neutral. ([#18447](https://github.com/cheaper-inference/OmniRoute/pull/18447)) — thanks @chernistry
- **fix(sse):** identical in-flight requests pinned to different provider connections are no longer answered by one shared upstream call; each connection now gets its own call while same-connection dedup still applies. ([#19099](https://github.com/cheaper-inference/OmniRoute/pull/19099))
- **fix(sse):** Local and self-hosted providers can no longer dispatch to cloud-metadata addresses; loopback and LAN base URLs still work. ([#19096](https://github.com/cheaper-inference/OmniRoute/pull/19096))
- **fix(sse):** non-streaming failures no longer relay the raw upstream error response to the client; the sanitized error body is sent and upstream headers like `Set-Cookie` are dropped while the `Retry-After` hint is kept. ([#19097](https://github.com/cheaper-inference/OmniRoute/pull/19097))
- **fix(sse):** A request pinned to one connection with `x-omniroute-connection` is no longer retried on a sibling account by the in-pipeline rotation (Codex 429, Antigravity 422). The pin is strict, as the API reference documents. ([#19099](https://github.com/cheaper-inference/OmniRoute/pull/19099))
- **fix(sse):** Provider requests that get a redirect now check the SSRF guard on every hop, and the API key is not sent to a different origin. ([#19096](https://github.com/cheaper-inference/OmniRoute/pull/19096))
- **fix(guardrails):** the Vision Bridge auto-router could pick a vision model on a no-auth provider that was disabled via `blockedProviders`; dispatch then refused the provider and the request failed with a misleading `401 provider_not_configured`. Bridge credential checks now treat a blocked no-auth provider as unusable (synthetic and stored-row paths alike, on fresh scans and cached picks; the audio bridge shares this credential gate) and the router skips it before per-model selection, so image requests reroute to an enabled describer. Keyed providers are unaffected: dispatch never consults `blockedProviders` for them. ([#18730](https://github.com/cheaper-inference/OmniRoute/pull/18730)) — thanks @woodsonl
- **fix(images):** admit the explicit Codex GPT-6.1 Sol image model and preserve original reference bytes on its edit path instead of applying legacy reference downscaling; reject unsupported masks before dispatch while retaining existing validation and legacy model behavior ([#17184](https://github.com/diegosouzapw/OmniRoute/pull/17184)) — thanks @KiaroSama
- **fix(usage):** Preserve pending-request byte accounting across module reloads, including retained state created before the shared counter was added (related to #17283). ([#18267](https://github.com/cheaper-inference/OmniRoute/pull/18267)) — thanks @AiCaptain357
- **fix(egress):** record the observed egress address of each address family ([#17326](https://github.com/diegosouzapw/OmniRoute/pull/17326)) — thanks @maxmad64bis (with thanks to @adevwithpurpose for the dual-target proposal in #9694)
- **db:** index `token_ledger` (migration 225) and collapse the gamification `getBalance` read to a single statement with two indexed subqueries, removing the full-table scans from the write-transaction path of `transferTokens` (#17908, thanks @oyi77)
- **fix(auth):** record bounded, coalesced audit events for successful API-key metadata and discovery reads (client IP and user-agent only with the new opt-in `API_KEY_AUDIT_CLIENT_DETAILS_ENABLED` flag, off by default) while honoring no-log keys, trusted-proxy attribution, multi-credential identity, application-log retention, and credential redaction; repeat touches inside the 60 s refresh window are absorbed in memory without touching SQLite and the audit schema is ensured once per database, so hot polls do not hit the database per request ([#17982](https://github.com/diegosouzapw/OmniRoute/pull/17982)) - thanks @seanford
- **fix(devin):** an unset `DEVIN_AGENTIC_HOME` now fails the Devin agentic bridge (`dva/...`) with a 503 `devin_bridge_not_configured` error that names the variable, instead of a 500 that read as a temporary server fault; a set but unsafe value keeps `unsafe_devin_home` (also 503). Both are treated as local configuration errors: no connection cooldown, no provider circuit breaker trip, no same-account retry, and no fallback wait before the next combo target ([#18019](https://github.com/diegosouzapw/OmniRoute/issues/18019)) ([#18433](https://github.com/cheaper-inference/OmniRoute/pull/18433)) — thanks @HouMinXi
- **fix(plugin):** refresh the served catalog on the configured TTL instead of publishing once at install ([#18235](https://github.com/cheaper-inference/OmniRoute/pull/18235)) — thanks @maxmad64bis
- **fix(combo):** Preserve an authorized same-target retry across a short transient connection cooldown, within existing wait limits and cancellation deadlines (#18257). ([#18258](https://github.com/cheaper-inference/OmniRoute/pull/18258)) — thanks @AiCaptain357
- **fix(vision-bridge):** lock the rejected provider/connection/model after a model-scoped 400 so later selections skip it while fallback continues ([#18271](https://github.com/cheaper-inference/OmniRoute/pull/18271)) — thanks @RaviTharuma
- **fix(executors):** Codex requests now reach the upstream as the client sent them: native passthrough gets no placeholder `instructions` or unrequested `reasoning.summary`/`include`, translated clients get neutral instructions instead of the Codex CLI persona, and `session_id` (HTTP and WebSocket) carries the client's own session instead of the account-wide workspace id that merged every conversation into one session, and a Chat client's explicit `parallel_tool_calls` reaches the Codex request ([#18274](https://github.com/diegosouzapw/OmniRoute/pull/18274)) — thanks @aldoeliacim
- **fix(compression):** Headerless lossy defaults and auto-triggers now retain configured lossless engines while lossy steps remain opt-in ([#18279](https://github.com/cheaper-inference/OmniRoute/pull/18279)) — thanks @jackyfan01 / @trespassmk
- **fix(combo):** the combo fallback rate is now the share of requests that skipped at least one target (bounded 0-100%) instead of skipped targets divided by requests, which could pass 100%; the old figure is exposed as `skipsPerRequest` and the combo card shows both with a translated label. Combo health thresholds in the control center now read the bounded rate ([#18284](https://github.com/cheaper-inference/OmniRoute/pull/18284)) — thanks @HouMinXi
- **fix(claude):** opt-in `CLAUDE_CACHE_TAIL_TTL_1H` feature flag caches the conversation tail with the same 1h TTL as system/tools (Vertex keeps 5m); default off keeps the 5m tail unchanged ([#18286](https://github.com/cheaper-inference/OmniRoute/pull/18286)) — thanks @tmaarcxs
- **fix(api):** `/v1/messages` and `/v1/messages/count_tokens` now return 401s in Anthropic's error envelope (`{"type":"error","error":{"type":"authentication_error","message":…}}`), so Claude Code shows the auth failure; clients that parsed `error.code` on that route should read `error.type`. Other routes keep their 401 body. ([#18299](https://github.com/cheaper-inference/OmniRoute/pull/18299)) — thanks @omar-nahhas
- **fix(sse):** A native Claude quota 429 on one model (e.g. the Fable weekly allotment) no longer takes the whole OAuth connection out of routing when fresh usage telemetry shows the session and weekly windows still have quota left — only that model is locked (bounded by `maxCooldownMs`), other Claude models keep routing ([#18301](https://github.com/diegosouzapw/OmniRoute/pull/18301)) — thanks @awci; fixes #18296.
- **fix(providers):** an explicit provider safety stop (e.g. Codex 403 `misalignment_policy_violation`, `content_policy_violation`) is classified as a request-scoped `provider_safety_refusal` instead of `forbidden`, so one refused request no longer marks a healthy account `banned` and disables it permanently, repeated refusals never count toward the refusal-streak ban, and a safety refusal no longer triggers a credential refresh that re-sent the refused request ([#18307](https://github.com/diegosouzapw/OmniRoute/pull/18307)) — thanks @aldoeliacim
- **fix(sse):** Classify Codex overloads only from real error declarations, preserve SSE framing around heartbeats, retain response identity on interrupted streams, preserve partial-output status and text whitespace, and separate native Anthropic accounting from client usage projection. Two opt-in flags, both off by default: `PROXY_STRICT_POST_REPLAY_ENABLED` resends a non-idempotent request only after a provably pre-send connection error, and `RESPONSES_TRANSPORT_KEEPALIVE_ENABLED` sends Responses keepalives as a blank line ([#18312](https://github.com/cheaper-inference/OmniRoute/pull/18312)) — thanks @shevar0.
- **fix(dashboard):** the combo model picker lists the live models of Zed Hosted and other live-only catalog providers ([#18315](https://github.com/cheaper-inference/OmniRoute/pull/18315)) — thanks @haidarmy
- **fix(codex):** Preserve nonnegative safe-integer reasoning budgets (including zero), model-defined effort spelling, explicit Responses summary delivery, and boolean `defer_loading` when normalizing requests. Keep forced/suffix effort precedence, compact/WebSocket field removal, `strict: null`, and tool-schema repairs unchanged. ([#18329](https://github.com/cheaper-inference/OmniRoute/pull/18329)) — thanks @shevar0
- Honor Codex session and weekly tracking toggles consistently in cached routing and live quota preflight, including explicit cutoffs, while retaining unknown-window exhaustion and paid-credit controls (#18330). ([#18334](https://github.com/cheaper-inference/OmniRoute/pull/18334)) — thanks @shevar0
- fix(providers): classify Codex quota windows by their reported duration so weekly-only Pro quota enforces weekly cutoffs and agrees across usage badges and account pools; preserve unknown durations without inventing 5h availability (#18326, #18330). ([#18334](https://github.com/cheaper-inference/OmniRoute/pull/18334)) — thanks @shevar0
- fix(api): regenerate the public OpenAPI spec with the build version and deployment-relative server URL. Builds generate it, and `npm run gen:public-openapi` refreshes the committed copy (#18331). ([#18349](https://github.com/cheaper-inference/OmniRoute/pull/18349))
- **fix(sse):** an upstream `400 "This model does not support assistant message prefill"` is now classified as a request-shape error instead of "model unavailable", so it no longer locks a healthy model for 1h on every combo target; plain `anthropic-compatible-*` connections gain an opt-in `requireUserLastMessage` setting that drops trailing assistant turns (no structured `tool_use`) before sending ([#18332](https://github.com/cheaper-inference/OmniRoute/pull/18332)) — thanks @MikeTuev
- **fix(codex):** Omit unsupported optional `web_search_call.action` history without replacing the search or dropping its item/encrypted content; retain valid search/open/find actions and compaction replay behavior. ([#18333](https://github.com/cheaper-inference/OmniRoute/pull/18333)) — thanks @shevar0
- **fix(codex):** Parse official primary/secondary quota headers, keep malformed usage unknown instead of inventing healthy zero-percent windows, validate reset units, and preserve independent Codex/Spark evidence and explicit quota denial. ([#18334](https://github.com/cheaper-inference/OmniRoute/pull/18334)) — thanks @shevar0
- **fix(sse):** the in-place mid-conversation system turns for plain `anthropic-compatible-*` relays are now a per-connection setting (`providerSpecificData.keepMidConversationSystem`, toggle in the edit-connection modal) instead of the global `ANTHROPIC_COMPATIBLE_MID_CONVERSATION_SYSTEM` feature flag, and no longer require the client to send the `mid-conversation-system-2026-04-07` beta — Claude Code builds that keep such turns without negotiating the beta were hoisted again and re-wrote ~110K tokens of cache on every turn. The beta is still forwarded when the client sends it, never invented ([#18341](https://github.com/cheaper-inference/OmniRoute/pull/18341)) — thanks @MikeTuev
- **fix(combo):** When every combo target fails authentication, the terminal diagnostics now report `authentication_rejected` with a `switch-combo` recovery action that tells the user to reconnect the account in /dashboard/providers, instead of the transient "retry" hint. Quota exhaustion keeps precedence, and mixed or empty outcomes are unchanged ([#18360](https://github.com/diegosouzapw/OmniRoute/issues/18360)) ([#18366](https://github.com/cheaper-inference/OmniRoute/pull/18366)) — thanks @HouMinXi
- **fix(types):** cache-detection context and Gemini usage fallbacks stay within the API Route Typecheck contract ([#18368](https://github.com/diegosouzapw/OmniRoute/pull/18368)) — thanks @RaviTharuma
- **fix(combo):** protected priority now falls back to the next target on structured context-window overflow without rewriting the original messages ([#18368](https://github.com/diegosouzapw/OmniRoute/pull/18368)) — thanks @RaviTharuma
- **fix(sse):** a non-streaming request no longer fails with a 502 `invalid_sse` when a forced-streaming upstream answers with one complete JSON response document (chat completion, Responses object, Claude message or structured error) labelled `text/event-stream`, even with `data: [DONE]` glued after it; the document is used as the response, so a combo no longer skips that target. Corrupt event streams still return the 502 ([#18377](https://github.com/cheaper-inference/OmniRoute/pull/18377)) — thanks @cristianob
- **fix(resilience):** Settings > Resilience > Request Queue can be saved again. The settings API rejected `requestQueue.globalConcurrentRequests`, which the card always sends, and `waitForCooldown.budgetMs`, which the runtime already reads; both are now accepted within the normalizer bounds (0 to 100000 and 0 to 300000 ms), and the legacy `defaults.globalConcurrentRequests` key is applied instead of being dropped ([#18390](https://github.com/cheaper-inference/OmniRoute/issues/18390), [#18401](https://github.com/cheaper-inference/OmniRoute/pull/18401)) — thanks @HouMinXi
- **fix(sse):** the direct response-start window now scales with request payload size (>512 KB ×2, >2 MB ×3, capped at 180 s; `OMNIROUTE_DIRECT_HEADERS_VOLUME_ADAPTIVE=0` pins the flat window) ([#18404](https://github.com/cheaper-inference/OmniRoute/pull/18404)) — thanks @jjxyxsjr
- **fix(executors):** a 400 advertising a `max_tokens` ceiling ("expected a value <= N") is now parsed, clamped (squeezing an over-cap thinking budget), retried once, and learned so future requests pre-clamp ([#18407](https://github.com/cheaper-inference/OmniRoute/pull/18407)) — thanks @jjxyxsjr
- **fix(autoCombo):** `auto/<family>` pools (for example `auto/claude-sonnet`) no longer route to the Devin CLI Agentic Bridge on hosts where the Devin CLI is not installed or not logged in. Family pools still bypass the curated no-auth allowlist (#6453, #8183), but the local CLI readiness gate from #15446 now applies to every pool instead of being skipped along with the allowlist ([#18417](https://github.com/diegosouzapw/OmniRoute/issues/18417)) ([#18428](https://github.com/cheaper-inference/OmniRoute/pull/18428)) — thanks @HouMinXi
- **fix(nvidia):** drop `openai/gpt-oss-120b`, `moonshotai/kimi-k3`, `deepseek-ai/deepseek-v4-pro-0813` and `deepseek-ai/deepseek-v4-flash-0731` from the NVIDIA catalog, because NVIDIA retired their free endpoints and every request now gets a 410 end of life answer. The budget exhaustion emergency fallback, which defaulted to `nvidia` + `openai/gpt-oss-120b` and so always failed, now defaults to `nvidia` + `nvidia/nemotron-3.5-super-vl-120b-a12b`, a model on NVIDIA's free endpoint that OmniRoute already lists as free, with a zero-price row so USD-limited API keys are not failed closed after a fallback ([#18418](https://github.com/diegosouzapw/OmniRoute/issues/18418)) ([#18443](https://github.com/cheaper-inference/OmniRoute/pull/18443)) — thanks @HouMinXi
- **fix(sse):** web_fetch interception works for Anthropic Messages clients again. The injected `omniroute_web_fetch` tool and a forced `tool_choice` are now built in the client's format before translation, as the web_search fallback already does, so a Claude to Claude request no longer gets a 400 for a nested function tool and a Claude to OpenAI request no longer loses the tool. A forced Anthropic choice `{ type: "tool", name: "web_fetch" }` now points at the injected tool, and an OpenAI Chat client on a Responses target keeps the tool name ([#18421](https://github.com/diegosouzapw/OmniRoute/issues/18421)) ([#18427](https://github.com/cheaper-inference/OmniRoute/pull/18427)) — thanks @HouMinXi
- **fix(sse):** Opt-in `OPENCODE_FREE_TIER_EGRESS_PROBE` (default off): after an OpenCode `403 FreeTierError` through a proxy, probe the request once through another account proxy or pool member (never a direct connection) and set the first egress aside only when that probe answers 200 ([#18423](https://github.com/cheaper-inference/OmniRoute/pull/18423)) — thanks @yxxrn
- **fix(translator):** the system-role fold (GLM 4.x/5.0 family) no longer flattens the first user message to text — images and other structured parts survive with the system instructions prepended as a leading text part ([#18437](https://github.com/cheaper-inference/OmniRoute/pull/18437)) — thanks @jjxyxsjr
- **fix(usage):** oversized image data URLs in persisted chat-log payloads now collapse into an `_omniroute_image_ref` record (mime, byte count, sha256 fingerprint, head) instead of a corrupt half-image ([#18438](https://github.com/cheaper-inference/OmniRoute/pull/18438)) — thanks @jjxyxsjr
- **fix(auto-combo):** account-semaphore capacity errors (`SEMAPHORE_TIMEOUT`/`SEMAPHORE_QUEUE_FULL`, type `account_semaphore_capacity`) are now request-scoped: no model lockout, no provider cooldown, no connection blame. The combo still retries the same target and falls through to the next one ([#18442](https://github.com/cheaper-inference/OmniRoute/pull/18442)) — thanks @jjxyxsjr
- **fix(pricing):** add zero-price rows for the seven NVIDIA catalog models that had none (`meta/muse-glimmer-30b`, `poolside/laguna-xs-2.1`, `google/gemma-4-31b-it`, `google/diffusiongemma-26b-a4b-it`, `nvidia/nemotron-3-ultra-550b-a55b`, `nvidia/nemotron-3.5-lightning-30b-a3b`, `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning`). They run on NVIDIA's free hosted endpoint, which has no per-token price listed, but without a row their usage was unpriced and an API key with a USD limit failed closed after one request ([#18464](https://github.com/diegosouzapw/OmniRoute/issues/18464)) ([#18470](https://github.com/cheaper-inference/OmniRoute/pull/18470)) — thanks @HouMinXi
- **fix(api):** the leaderboard SSE stream (`/api/gamification/stream`) and the embedded-service log tail (`/api/services/[name]/logs`) now release their timers and ring-buffer subscription when the response body is cancelled or a write fails, not only when the request signal aborts. A half-open peer (tunnel drop, network change) never aborts the signal, so these streams used to keep polling and queueing for the life of the process. The leaderboard stream also closes immediately when the client disconnected before auth finished. ([#18465](https://github.com/cheaper-inference/OmniRoute/pull/18465)) — thanks @Abelo9996
- **fix(api):** also bound live SSE queues to 256 KiB, prevent overlapping leaderboard polls, and sanitize error events. Large log snapshots retain their complete event payload and Unicode while encoding in 32 KiB chunks on demand; cancellation releases the retained snapshot and live queue (Refs #18445). ([#18465](https://github.com/cheaper-inference/OmniRoute/pull/18465)) — thanks @Abelo9996
- **fix(api-keys):** a non-default API key selected in the dashboard playground no longer fails every request with "API key model policy unavailable". The resolved-model check in the chat handler now uses the same key the entry policy loaded, which also makes a Vision Bridge self-loop check its caller's key instead of the unrestricted self-loop bearer ([#18471](https://github.com/diegosouzapw/OmniRoute/issues/18471)) ([#18481](https://github.com/cheaper-inference/OmniRoute/pull/18481)) — thanks @HouMinXi
- **fix(embeddings):** the embedding combo family guard compares the pinned `dimensions` (client body first, then the combo's) instead of each target's registry default, so a combo can fail over between providers of the same model, e.g. `gemini/gemini-embedding-001` → `openrouter/google/gemini-embedding-001` at 768 dimensions ([#18483](https://github.com/diegosouzapw/OmniRoute/issues/18483), [#18484](https://github.com/diegosouzapw/OmniRoute/pull/18484)) — thanks @TheViniAlmeida
- **fix(dashboard):** Add an optional masked Microsoft 365 refresh-token field to connection Advanced Settings, safely persist replacements, and preserve stored credentials when left blank ([#18492](https://github.com/cheaper-inference/OmniRoute/pull/18492)) — thanks @trespassmk
- **fix(sse):** the response-model echo (`echoRequestedModelName`, Codex CLI, Claude Code) now returns the model the client requested instead of the combo leaf target ([#18495](https://github.com/cheaper-inference/OmniRoute/pull/18495), closes [#18494](https://github.com/cheaper-inference/OmniRoute/issues/18494)) — thanks @xiaoyaner0201 / @skygunner
- **fix(dashboard):** Stop the dashboard from looking up client versions (Codex and Zed on the GitHub API; Claude Code, Copilot, Gemini, Qwen, CodeBuddy, Kimi and OpenCode on npm) from the viewer's browser on every page load. Next.js gives browser bundles a `process` object, so the `typeof process` guards never stopped them; they spent the per-IP GitHub API budget the server needs and surfaced as "403 rate limit exceeded" in the console. A failed Codex release lookup now also waits 15 minutes before retrying instead of retrying on every call ([#18503](https://github.com/cheaper-inference/OmniRoute/pull/18503)) — thanks @ikkadecibel-blip
- **fix(providers):** Stop the Copilot, Claude Code, Gemini, Qwen, CodeBuddy, Kimi and Zed client-version getters from repeating their npm/GitHub lookup on every call when the registry answers the pinned version (their cache only kept newer versions), and stop all of them, OpenCode included, from retrying a failed lookup on every request: an answer now counts as checked for the existing 6-hour TTL, and a failure is retried after 15 minutes ([#18507](https://github.com/diegosouzapw/OmniRoute/pull/18507)) — thanks @ikkadecibel-blip
- **fix(discovery):** Mistral API models discovered via `/v1/models` now map `max_context_length` to the real 256K / 1M context windows instead of defaulting to 128K, parse boolean `capabilities` to stamp operation-specific endpoints (`audio-transcriptions`, `audio-speech`, `ocr`) so Voxtral and Mistral OCR models are not misclassified as chat models, and preserve uncapped output limits when upstream publishes no separate output limit. ([#18510](https://github.com/cheaper-inference/OmniRoute/pull/18510)) — thanks @RayJW
- **fix(api):** the "Expose in Claude Code" provider toggle now takes effect for OpenAI-compatible providers. The dashboard stores the setting under the provider's node id while `/v1/models` and the request-time `claude/<prefix>/<model>` resolver read it by the node's prefix, so no mirror was listed or routed ([#18522](https://github.com/cheaper-inference/OmniRoute/pull/18522), fixes [#18517](https://github.com/cheaper-inference/OmniRoute/issues/18517)) — thanks @yourspraveen
- **fix(api):** a saved request body limit now also governs `/v1/chat/completions` and `/v1/responses`, for the header check and for the bytes actually read, including chunked bodies. **Upgrade note:** a limit saved earlier now applies to these two routes, so an install with a saved limit below 50 MB starts getting `413` (`PAYLOAD_TOO_LARGE`) on chat bodies above it. To raise it, save a higher value at `/dashboard/settings/advanced` or `PATCH /api/settings` with `maxBodySizeMb`; 50 restores the previous chat limit. Installs that never saved one keep the 50 MB default, and `OMNIROUTE_CHAT_HARD_MAX_BODY_BYTES` keeps working and also caps a saved limit ([#18527](https://github.com/cheaper-inference/OmniRoute/pull/18527)) — thanks @shevar0
- **fix(dashboard):** Show no-auth providers (Cloudflare AI Playground and the other key-less providers) in the Home "Provider Topology" and count their traffic in `/api/provider-metrics` and the free-provider rankings: both call-log queries only counted providers with a `provider_connections` row and the topology only kept providers with an active connection, so a no-auth provider never appeared even while it served requests. Blocked no-auth providers stay hidden, and a deleted provider still does not resurface from old logs (#10714) ([#18530](https://github.com/diegosouzapw/OmniRoute/pull/18530)) — thanks @ikkadecibel-blip
- **fix(providers):** Preserve DeepSeek Web error hints, return HTTP 429 for explicit upstream rate-limit rejections, and fail rejected streams without reporting a successful completion. ([#18534](https://github.com/cheaper-inference/OmniRoute/pull/18534)), thanks @Hector-Ha.
- **fix(dashboard):** Keep Codex quota panels in sync with accepted upstream response observations using local cached reads. Preserve separate Codex/Spark windows, partial observations and their ages; prevent delayed usage refreshes from hiding newer inference quota. Pause automatic cached reads in hidden tabs without adding upstream calls or blocking generation. ([#18535](https://github.com/cheaper-inference/OmniRoute/pull/18535)) — thanks @shevar0
- **fix(routing):** `expiry-first` account selection on direct model requests now picks the account whose quota is closest to being lost again. A 5h session reset no longer outranks a longer window's deadline in the metric-aware score, an evicted in-memory quota entry is read back from `quota_snapshots` instead of scoring the account as "no telemetry", and the `$60 / month` window that opencode-go and opencode-zen store as `mcp_monthly` counts as the monthly deadline, while GLM's `mcp_monthly` MCP tool allowance stays out of the ranking ([#18548](https://github.com/cheaper-inference/OmniRoute/pull/18548)) — thanks @HouMinXi
- **fix(authz):** `REQUIRE_API_KEY` is now read the same way by the request gate and by both startup exposure warnings (Next server and `omniroute serve`): `true`, `1` and `yes` turn it on regardless of case or surrounding whitespace. Before, a value such as `TRUE`, `True`, `YES` or `" true"` left the gate off, so `/v1` served requests without a key, while both warnings treated the value as on and printed nothing. Deployments that set one of those spellings now actually require a key, and clients without one get `401`. Any other value, including `on`, still means off. ([#18550](https://github.com/cheaper-inference/OmniRoute/pull/18550)) — thanks @HouMinXi
- **fix(mcp):** `omniroute --mcp` no longer exits silently when the install path contains a space (or `%`, `#`, non-ASCII, or a symlinked install) — the direct-run guard compares real filesystem paths instead of a percent-encoded `file:` URL against the raw `argv[1]`, so the stdio transport starts and answers `initialize` (#18556) ([#18634](https://github.com/cheaper-inference/OmniRoute/pull/18634)) — thanks @skygunner / @serdarucel
- **fix(combo):** with `zeroLatencyOptimizationsEnabled` + `hedging` racing targets, a superseded hedge loser is no longer logged as a 499 "Request aborted" call. The winner path now cancels sibling targets with the hedge-cancel abort reason, the per-target runner forwards the parent's actual reason (client disconnects stay bare aborts instead of being stamped as hedge cancellations), and the streaming leg short-circuits on that reason — teardown only, no attempt row, status-599 `hedge_cancelled` sentinel result — so 499/error dashboards count real client disconnects, not internal supersessions. ([#18559](https://github.com/cheaper-inference/OmniRoute/pull/18559)) — thanks @jjxyxsjr
- **fix(providers):** Route `gpt-6-astra`, `gpt-6.1-sol`, and `gpt-5.6-sol` through AgentRouter's Responses endpoint by default while preserving explicit connection protocol settings ([#18560](https://github.com/cheaper-inference/OmniRoute/pull/18560)) — thanks @trespassmk
- **fix(codex):** Codex accounts with the paid-credits opt-in are used only after every other account's 5h/weekly subscription quota for the request is spent, instead of competing with accounts that still have free quota; and an OAuth re-login no longer silently switches the opt-in off ([#18561](https://github.com/cheaper-inference/OmniRoute/pull/18561) — thanks @feci).
- **fix(sse):** a Qoder queue throttle (`503 upstream_busy`, upstream code `10605`) no longer parks the whole connection. Qoder multiplexes every model behind one connection, and `qoder` was missing from the `hasPerModelQuota()` allowlist, so one queued model put the account into a connection-wide cooldown and every sibling model looked dead until it expired. The queued model now takes the cooldown alone and its siblings stay routable. ([#18572](https://github.com/cheaper-inference/OmniRoute/pull/18572)) — thanks @Junior-HJ
- **fix(sse):** an Anthropic OAuth `403 "Request not allowed"` (`REQUEST_REJECTED`) no longer deactivates the whole connection. The refusal was accumulating on a connection-wide streak, so a single upstream rejection disabled every account behind that provider; it is now scoped to the model that was refused, and the sibling models on the same connection stay routable. The streak still escalates to a longer cooldown for the refused model, and a success clears it. ([#18574](https://github.com/cheaper-inference/OmniRoute/pull/18574)) — thanks @Junior-HJ
- **fix(catalog):** replace the stale Token Harbor `claude-haiku-5.5:free` route with the `deepseek-v4.1-flash:free` and `mimo-v2.6-flash:free` routes listed on the public Free catalog, with per-model evidence records (#18579) — thanks @Petrusreno
- **fix(ci):** Verify the actual Node runtime in pushed base and web Docker images on both architectures before publishing multi-arch manifests, rejecting versions below the Node 26.12.1 security floor for CVE-2026-111497 ([#18585](https://github.com/cheaper-inference/OmniRoute/pull/18585)) — thanks @alirezariahi.
- **fix(routing):** Apply exact context-window overrides to direct-request input checks and advertised input limits while preserving explicit input ceilings ([#18588](https://github.com/cheaper-inference/OmniRoute/pull/18588), fixes #18582) — thanks @alirezariahi. — thanks @K007-K
- **fix(routing):** Preserve unreported quota reset telemetry without treating unknown fractions as exhausted or letting short sessions inflate expiry-first account scores ([#18588](https://github.com/cheaper-inference/OmniRoute/pull/18588), fixes #18552) — thanks @alirezariahi. — thanks @K007-K
- **fix(providers):** Classify the two active NVIDIA free endpoints as rate-limited recurring access with per-model evidence and API-key requirements ([#18588](https://github.com/cheaper-inference/OmniRoute/pull/18588), fixes #18499) — thanks @alirezariahi. — thanks @K007-K
- **fix(cli):** on Windows, `serve` now repoints a Startup `OmniRoute.vbs` that still launches an older, removed or unreadable installed copy (pnpm keeps each version in its own directory, so autostart kept booting the pre-upgrade version); only the CLI path changes, the node binary and flags are kept, and it never repoints at an older version or an `npx` cache copy (#18605) — thanks @AndresDiagoM
- **fix(sse):** replay server-restored Claude signed thinking at its original content index — the continuity store kept a flat list and `openai-to-claude` pushed every block to the front of each assistant fragment that owned any of the turn's `tool_use` ids, so `[thinking, text, tool_use, thinking, tool_use]` was reordered and a turn the client split into several messages replayed the blocks twice; the store now records which `tool_use` each run preceded and the translator puts each run back directly before that `tool_use`, once (Refs [#18607](https://github.com/cheaper-inference/OmniRoute/issues/18607)) ([#18616](https://github.com/cheaper-inference/OmniRoute/pull/18616)) — thanks @serenity-X
- Honor enabled stream recovery for structured transport read failures before usable combo output, with bounded retries and client cancellation cleanup. First-output inspection limits conservatively prevent a new transport retry. ([#18610](https://github.com/cheaper-inference/OmniRoute/pull/18610)) — thanks @AiCaptain357
- **fix(docs):** the docs layout no longer mounts the Fumadocs next-themes provider, whose `theme` storage key collided with the root theme store; a light/dark switch bound to the root theme store (and the `d` hotkey) replaces the Fumadocs one, so `/docs` follows the dashboard theme (#18612, refs #18078) — thanks @mihsanalam
- **fix(api):** A combo member listed by a connection's synced catalog but carrying no limit evidence (no registry row, static spec, synced capability, or persisted override) no longer pulls the combo's advertised `max_input_tokens` down to its provider's `defaultContextLength`; confirmed sibling caps still bound the combo (#18609). ([#18618](https://github.com/cheaper-inference/OmniRoute/pull/18618)) — thanks @serenity-X
- **fix(sse):** recognize Gemini `finishReason` terminal markers in `streamRecovery` holdback and `throughputWatchdog` so completed Gemini SSE streams under 750ms are not misclassified as `TruncatedStreamError` and replayed 4x ([#18624](https://github.com/diegosouzapw/OmniRoute/pull/18624) — thanks @fidelix)
- **fix(security):** the input sanitizer now honors dashboard Feature Flags overrides — `INPUT_SANITIZER_ENABLED` and `PII_REDACTION_ENABLED` resolve DB override first with the env var as fallback (the same pattern as `INJECTION_GUARD_MODE`), so toggling them in the Feature Flags UI takes effect without an env edit or restart (#18632) ([#18635](https://github.com/cheaper-inference/OmniRoute/pull/18635)) — thanks @skygunner
- **fix(antigravity):** the OpenAI → Antigravity/Gemini stream translator forwards the trailing usage-only chunk (`choices: []`, sent after the finish chunk by `include_usage` upstreams) as `usageMetadata` instead of dropping it, without double-counting when usage already rode on the finish chunk ([#18643](https://github.com/diegosouzapw/OmniRoute/pull/18643)) — thanks @AndresDiagoM
- **fix(pricing):** OpenCode Go requests are priced from LiteLLM-synced pricing again; LiteLLM's `opencode_go` bucket was stored under a key no cost lookup reads, so every `opencode-go` request booked as unpriced $0 ([#18644](https://github.com/cheaper-inference/OmniRoute/pull/18644)) — thanks @yourspraveen
- **fix(compression):** a legacy install with the master switch on, no stored `engines` row and `defaultMode: "stacked"` no longer runs a stacked pipeline while the dashboard shows every engine off. When the backfilled engines map has zero enabled engines, dispatch now resolves to `off` — matching the panel; legacy installs whose backfill still enables an engine (e.g. caveman on) keep their stacked dispatch. ([#18637](https://github.com/diegosouzapw/OmniRoute/issues/18637)) ([#18650](https://github.com/cheaper-inference/OmniRoute/pull/18650)) — thanks @serenity-X
- **fix(combo):** A 403 without a quota signal in the body now locks the exact provider/connection/model tuple instead of the whole quota family, so a restricted key, forbidden route, or unauthorized model no longer parks sibling models of the same connection for the cooldown. A 403 that carries a quota proof still locks the family. ([#18652](https://github.com/cheaper-inference/OmniRoute/pull/18652)) — thanks @maxmad64bis
- **fix(antigravity):** Store the Google verify-account link from the raw `403 VALIDATION_REQUIRED` (executor and connection test) so the Verify account button finally appears, mask the link in the probe log, and keep a still-blocked account in error instead of letting the quota poll, token refresh or recovery tick flip it back to active ([#18661](https://github.com/cheaper-inference/OmniRoute/pull/18661)) — thanks @bufftop25
- **fix(auth):** a short model lockout on the last healthy account is no longer reported as "all accounts have exhausted their quota" with another account's multi-day reset. When that lockout ends before the earliest quota reset, the request gets a model-scoped 429 with the lockout's retry time, so clients retry in seconds instead of giving up (#18664). ([#18814](https://github.com/cheaper-inference/OmniRoute/pull/18814)) — thanks @DongwonTTuna
- **fix(antigravity):** Route each request to an Antigravity/agy account whose synced model list includes the requested model, so Claude 5.5 and Gemini 4 Argon requests no longer start on or fail over to accounts whose plan lacks them; fails open when no account lists the model, keeps never-synced accounts eligible, and lets an operator-pinned account through (a combo step pinned to an account whose plan lacks the model is skipped while another account lists it) ([#18665](https://github.com/cheaper-inference/OmniRoute/pull/18665)) — thanks @bufftop25
- **fix(sse):** Leonardo image and video generation work again; image requests sent the catalog id (`phoenix`, `sdxl`) where Leonardo expects a platform-model UUID and got HTTP 500 "internal error", and video posted an image body to the image endpoint (400 "Unexpected variable num_frames"); video now uses `/generations-text-to-video` with Motion 2.0 / Motion 2.0 Fast and returns the mp4 instead of the thumbnail; an image `size` such as `576x1024` is now honoured instead of always 1024x1024, and the mp4 download stops on client abort, times out, and reports a failed download as an upstream error ([#18670](https://github.com/cheaper-inference/OmniRoute/pull/18670)) — thanks @yourspraveen
- **fix(sse):** Add content-free native-stream diagnostics at empty-response rejection boundaries. Explicit failed, cancelled and incomplete Responses statuses cannot authorize a successful empty turn. Diagnostics distinguish terminal evidence, streamed text/tool counters and observer gaps without logging generated content, tool inputs or provider errors. Retry budgets and account routing are unchanged. (#18685) — thanks @fouadSalkini
- **fix(sse):** opt-in `PROXY_REFUSAL_MODEL_SCOPE` (default off) scopes an opencode quota 429 to the model that hit it, so the same egress keeps serving other models. Readers with no model (pool rotation, park and replay, admin visibility, selector trigger, health sweep) still see the refusal, and a model-less recovery ends every model's entry ([#18686](https://github.com/cheaper-inference/OmniRoute/pull/18686)) — thanks @maxmad64bis
- **fix(providers):** Codex `gpt-6.1-sol` is registered as a Codex hosted-tool image model (`codex/gpt-6.1-sol-image` in the image catalog), so image combos keep `cx/gpt-6.1-sol` as a target instead of dropping it, and `/v1/images/edits` accepts it instead of returning 400 ([#18687](https://github.com/cheaper-inference/OmniRoute/issues/18687)) ([#18826](https://github.com/cheaper-inference/OmniRoute/pull/18826)) — thanks @anhtahaylove
- **fix(claude-thinking):** skip the signed-thinking restore for assistant turns whose tool_call id appears more than once in the client history (a retried turn re-sends the same id for a different generation), which made Anthropic answer 400 "thinking or redacted_thinking blocks in the latest assistant message cannot be modified" (#18688). — thanks @AbdullahPesteli
- **fix(sse):** the JSON-schema fallback for opencode-zen's DeepSeek models now builds its instruction with real newlines instead of a literal backslash-n, so the schema, its code fence and the system-prompt separator reach the model as line breaks (follow-up to #18241) (#18691). — thanks @AndresDiagoM
- **fix(translator):** Preserve Responses developer instructions when translating requests to Gemini and Antigravity ([#18704](https://github.com/diegosouzapw/OmniRoute/pull/18704)) — thanks @AiCaptain357
- **fix(antigravity):** Keep default Antigravity and agy generation requests on the daily consumer endpoint instead of falling back to the regular host ([#18706](https://github.com/cheaper-inference/OmniRoute/pull/18706)) — thanks @AiCaptain357
- fix(sse): a combo can opt out of the cooldown wait with `config.comboCooldownWaitEnabled: false`, so it fails fast instead of waiting out a model lock; the global `comboCooldownWait` setting still has to be enabled for any combo to wait. (#18708) — thanks @stanleytejakusuma
- **fix(db):** Preserve unknown backup-directory formats instead of deleting them as orphans; retain `.sqlite.zst` archives and their dated preservation receipts as one count/age-retention family, while keeping detached receipts. ([#18712](https://github.com/cheaper-inference/OmniRoute/pull/18712)) — thanks @aldoeliacim
- **fix(sse):** `muse-code` Responses requests no longer reach Muse with the wrong tool when a client tool — or a `function_call` already in the turn history — shares a name with one of its eight reserved builtins (`read_file`, `write_file`, `edit_file`, `bash`, `web_search`, `tool_search`, `subagent_send_message`, `subagent_cancel`): only on that provider/format pair those names are aliased to a deterministic `muse_<name>_<sha256>` wire name and the client originals are restored on every reply path (JSON, streaming, passthrough tail, completion summaries, server-owned follow-up turns), with native namespace identities kept in their own ledger and no other provider or endpoint affected ([#18720](https://github.com/cheaper-inference/OmniRoute/pull/18720), refs [#18600](https://github.com/cheaper-inference/OmniRoute/issues/18600)) - thanks @trespassmk
- **fix(codex):** cached quota exhaustion no longer rejects accounts with positive remaining quota at the shared 99% safety threshold; configurable early cutoffs and genuine exhaustion remain enforced ([#18735](https://github.com/cheaper-inference/OmniRoute/pull/18735) — thanks @insoln).
- Logs now show intermediate chat HTTP sends from the shared HTTP executor and Antigravity as related request rows. Final rows retain their logical outcome and usage, while safe HTTP metadata keeps earlier failures visible without duplicating logical request counts or cost. The Errors filter now classifies rows in one place: a row with HTTP status 400 or above, or a non-empty error even on a 2xx status, is an error; an in-flight row is pending; a retained attempt without an HTTP response is a transport error ([#18736](https://github.com/cheaper-inference/OmniRoute/pull/18736), fixes [#18724](https://github.com/cheaper-inference/OmniRoute/issues/18724)) — thanks @AiCaptain357
- **perf(quota):** Scope availability read views to single accounts and prioritize in-memory usage over repeated SQLite snapshot queries to eliminate 14-21s event loop stalls on instances with many connections; the dashboard availability view now prefers fresh provider-limits usage over a stale persisted snapshot, which is only read when no usage is available ([#18741](https://github.com/cheaper-inference/OmniRoute/pull/18741)) — thanks @bufftop25
- **fix(sse):** Codex quota windows older than five minutes now count as unknown instead of exhausted, so an account that ChatGPT reset outside OmniRoute becomes routable again without waiting for a manual refresh. This applies by default, whatever `QUOTA_WINDOW_BOUNDED_PARK` is set to ([#18742](https://github.com/cheaper-inference/OmniRoute/pull/18742)) — thanks @stanleytejakusuma
- **fix(copilot):** `gpt-6.x` and `gpt-5.5+` ids (and codex ids) on GitHub Copilot and GitHub Enterprise Copilot now go to `/responses` even when a synced catalog row carries no endpoint metadata, instead of failing with HTTP 400 on `/chat/completions`. A synced `gpt-6-*` row that lists no chat endpoint now defaults to Responses (it stayed on Chat Completions since #15337); rows that declare a chat endpoint are unchanged. GitHub Enterprise Copilot also drops `temperature` for the same family and lists the `gpt-6*` models in its static registry (#18743). — thanks @nareshh74
- **fix(docker):** a published image tag now always holds both `linux/amd64` and `linux/arm64`. The publish workflow checks every flavor in both registries, skips a version only when the mandatory Node and Web images are complete, and publishes the best-effort Bun images all-or-nothing, so a failing Bun build can no longer block the supported images or leave a single-architecture `-bun` tag (#18749). — thanks @rqzbeh
- **fix(providers):** Dashboard Test Message accepts Cloudflare Workers AI model IDs beginning with `@cf/` ([#18752](https://github.com/cheaper-inference/OmniRoute/pull/18752)) — thanks @trespassmk
- **fix(capabilities):** Restrict the GLM-5.3 vision heuristic to Flash so text-only base/effort variants no longer inherit image support ([#18758](https://github.com/cheaper-inference/OmniRoute/pull/18758)) — thanks @insoln.
- **fix(sse):** with `PROVIDER_SAFETY_STOPS_TERMINAL` on, a provider safety stop that arrives inside a stream now ends priority, round-robin and nested combos instead of re-sending the conversation to a sibling model, reaches `/v1/responses` clients after the early keepalive as a terminal `response.failed` with its code (Codex CLI had treated the old `error` frame as a disconnect and retried five times), an error-only first frame is classified at stream readiness instead of being retried as an early EOF, and a specific rejection frame is no longer masked by a later generic `response.failed` ([#18760](https://github.com/diegosouzapw/OmniRoute/pull/18760)) — thanks @aldoeliacim
- **fix(compression):** the default safe pipeline no longer truncates earlier tool results to the lite cap without a request opt-in, and the `replace-autotrigger` context-budget dial brings truncation back (then further ladder stages) only for a request that does not fit the model window; named and routing-assigned combos are never replaced ([#18761](https://github.com/diegosouzapw/OmniRoute/pull/18761)) — thanks @aldoeliacim
- **fix(openai):** a 429 whose body carries only `error.code: credit_balance_exhausted` is now classified as terminal credit exhaustion instead of a retryable rate limit. That is the code OpenAI returns once an eligible ChatGPT Pro or Business subscription's monthly API grant is spent, and it cannot clear until someone buys credits — so the account is parked instead of being re-selected on a short backoff on every request. ([#18770](https://github.com/cheaper-inference/OmniRoute/pull/18770)) — thanks @HouMinXi
- **fix(sse):** a request's own retries, account rotations and combo legs now reuse the turn-execution slot the request already holds instead of failing with a local `turn_in_progress` 409 that read downstream as an account rate limit and cooled healthy accounts; a genuinely concurrent duplicate with the same idempotency key now waits up to 60 s for the in-flight turn before falling back to that 409 (#18778, refs #18716). — thanks @hongkongkiwi
- **fix(providers):** Kiro tool calls with no input (no-parameter tools, or optional parameters the model left out) now stream `function.arguments: "{}"` instead of `""`, so strict OpenAI clients no longer drop the call ([#18780](https://github.com/cheaper-inference/OmniRoute/pull/18780), closes #18718) — thanks @gaurav0107
- **fix(sse):** Preserve explicit Claude reasoning effort when routing to Codex, without allowing connection defaults to override it ([#18788](https://github.com/cheaper-inference/OmniRoute/pull/18788)) — thanks @trespassmk
- **fix(providers):** The Mistral model list no longer offers the retired `devstral-latest` (Devstral 2, retired by Mistral on 2026-07-31), and the user guide no longer lists it ([#18792](https://github.com/cheaper-inference/OmniRoute/pull/18792), closes #18755) — thanks @costajohnt
- **fix(translator):** Sanitize client tool schemas on the Responses → Chat path so deep-truncated schemas (`"enum": "[MaxDepth]"`, index-keyed objects) no longer reach strict OpenAI-compatible upstreams such as Ollama Cloud, which rejected them with `cannot unmarshal string into Go struct field ToolProperty.enum`. Fixes #18679 ([#18806](https://github.com/cheaper-inference/OmniRoute/pull/18806)) — thanks @gaurav0107
- **fix(providers):** Kiro no longer rejects calls to a client-declared tool named `tool_call` (OpenClaw, Hermes Agent deferred-tool bridges) as a malformed MCP wrapper; those calls now pass through with their arguments unchanged. Fixes #18675 ([#18823](https://github.com/cheaper-inference/OmniRoute/pull/18823)) — thanks @gaurav0107.
- **fix(authz):** With `OMNIROUTE_TRUST_PROXY=private`, a private-network client reaching OmniRoute through a private reverse proxy (e.g. a Tailscale or LAN nginx) can open LOCAL_ONLY dashboard surfaces such as Discovery again, instead of getting `403 LOCAL_ONLY` ([#18871](https://github.com/cheaper-inference/OmniRoute/pull/18871)). The proxied request counts as LAN only when the chain names a private, non-loopback client and did not enter through a Cloudflare tunnel; the default, public proxies and the loopback-only CLI-token gate are unchanged — thanks @aldoeliacim
- **fix(cli):** Usage analytics now shows prompt and completion tokens returned by the current API, while retaining compatibility with older server field names ([#18900](https://github.com/cheaper-inference/OmniRoute/pull/18900)) — thanks @fabioKoruja00
- **fix(dashboard): readable warning text in the light Editions palette**: warning badges, warning buttons and `text-warning` use a darker copper (`#8c5a1b`) in light Editions. They reach WCAG AA on the tinted background (4.74:1), the canvas and surfaces, where the kit's `#99631f` was 4.09:1. Classic and dark keep the kit colour. ([#18919](https://github.com/cheaper-inference/OmniRoute/pull/18919))
- **fix(dashboard): keep dark warning ink before the theme script runs**: without `data-theme` (before the boot script, or with JavaScript off) the kit follows the system preference, so a dark system no longer gets the light-only `#8c5a1b` warning ink on the dark Editions background. ([#18945](https://github.com/cheaper-inference/OmniRoute/pull/18945))
- **fix(dashboard):** The `/landing` page follows the active colour palette (Editions or Classic, light or dark) instead of painting a fixed dark page with its own colours: every hex, `rgb()`, Tailwind palette and white/black colour now comes from the vendored CI UI Kit tokens (surfaces, text, accent, the kit chart slots for the feature cards and provider chips, the code tokens for the terminal), with text at 4.5:1 or more in all four combinations, and `tests/unit/dashboard-kit-sweep.test.ts` fails if a literal colour comes back ([#19010](https://github.com/cheaper-inference/OmniRoute/pull/19010))
- **fix(compression): analytics reports lite again when lite runs as a scoped stacked step**: since #18818 the lossy-request policy runs a configured `lite` as a one-step stacked pipeline. Compression analytics then recorded `stacked` as both mode and engine, so the dashboard's mode and engine breakdowns no longer showed lite. The plan now carries `reportedMode: "lite"`, and the analytics row records that; dispatch is unchanged. ([#19012](https://github.com/cheaper-inference/OmniRoute/pull/19012))
- **fix(compression): keep the dispatched engine in analytics for multi-engine and skip rows**: the configured-mode label from #19012 now replaces the engine only when a single step ran. A combo that ran several engines, and the "no savings" skip rows, keep the engine the pipeline actually used. ([#19056](https://github.com/cheaper-inference/OmniRoute/pull/19056))
- **fix(providers):** Use the global startup timeout for Antigravity response headers instead of the shorter SSE-readiness timeout. ([#18705](https://github.com/cheaper-inference/OmniRoute/pull/18705)) — thanks @AiCaptain357
- **fix(claude):** a Claude Team connection now shows the seat's own tier (`user_rate_limit_tier`, e.g. `default_claude_max_5x`) instead of the organization's base tier (`organization_rate_limit_tier`, e.g. `default_raven`) in the usage plan label, the quota plan and the dashboard, and stores the seat (`seat_tier`) from the bootstrap, the token exchange and the auth-file import. The import reads the nested `oauth_account` bootstrap and keeps one account's connections in two organizations apart. A member with no seat assigned, whom every OAuth call refuses with `oauth_not_allowed_for_organization`, gets a usage message that names it instead of a bare HTTP 403. ([#18763](https://github.com/cheaper-inference/OmniRoute/pull/18763)) — thanks @shipsfromrio
- **fix(dashboard):** The dashboard no longer opens the browser's native `confirm()` / `alert()` dialogs. The 34 confirmations (API key delete and regenerate, pricing reset, batch cancel and retry, combo and pool deletes, and more) now use the kit `ConfirmModal` through a new `useConfirmDialog()` hook, and the 8 alerts now show as toasts. The action still runs only after Confirm, and Cancel does nothing. A guard test keeps native dialogs out of `src/app` and `src/shared` ([#19066](https://github.com/cheaper-inference/OmniRoute/issues/19066)) ([#19069](https://github.com/cheaper-inference/OmniRoute/pull/19069))
- **fix(usage):** Provider Limits now reads the Kimi Coding subscription name when refreshing quotas, while keeping quota and billing data available if the optional profile request fails. ([#18578](https://github.com/cheaper-inference/OmniRoute/pull/18578)) — thanks @AiCaptain357
- **fix(oauth):** importing a token (for example a Claude setup-token) now verifies it through the proxy assigned to the provider, as the OAuth code exchange already did, instead of calling the provider directly ([#19007](https://github.com/cheaper-inference/OmniRoute/pull/19007))
- **fix(sse):** Preserve the captured outgoing request when streaming provider execution fails, and leave provider-request diagnostics absent when no outgoing request was captured. ([#18703](https://github.com/cheaper-inference/OmniRoute/pull/18703)) — thanks @AiCaptain357
- Keep explicit `misalignment_policy_violation` responses request-scoped instead of treating them as account failures, and preserve OpenRouter stream errors with populated choices as failed Responses events. ([#18805](https://github.com/cheaper-inference/OmniRoute/pull/18805)) — thanks @w0wl0lxd
- **fix(authz):** changing the global system prompt now requires an `admin` access token; a `write` token can still read it ([#18963](https://github.com/cheaper-inference/OmniRoute/pull/18963))
- **fix(translator):** Responses to Chat translation re-sends only the newest tool-output images (16 images, 20 MB by default, set with `OMNIROUTE_TOOL_OUTPUT_IMAGE_MAX_COUNT` / `OMNIROUTE_TOOL_OUTPUT_IMAGE_MAX_BYTES`). Each older image becomes a text placeholder with its `tool_call_id`, so a long Codex session no longer grows the request until the upstream rejects the body ([#18684](https://github.com/cheaper-inference/OmniRoute/issues/18684)) ([#18784](https://github.com/cheaper-inference/OmniRoute/pull/18784)) — thanks @MannXo
- **fix(dashboard):** Log, proxy and conversation badges (provider, protocol, proxy type, level, HTTP status) use the kit chart and status tones instead of a parallel hex palette, so their text reaches 4.5:1 in Classic and Editions, light and dark (white on `#10B981` was 2.54:1). Provider and CLI tool brand colours are no longer text or a fill under white text: the provider header links use the primary ink, the rankings and CLI step avatars use a brand tint with body text, the onboarding provider tiles and the batch wizard's done steps use the kit tint + ink ([#19066](https://github.com/cheaper-inference/OmniRoute/issues/19066)) ([#19094](https://github.com/cheaper-inference/OmniRoute/pull/19094))
- **test(dashboard):** The kit colour guard now covers the public routes (`landing`, `docs`, `login`, `status`, `connect`, `forbidden`) and `src/shared/constants/*.ts`, catches arbitrary-hex classes, `rgba()`/`hsl()` and literal colours in `style={{ … }}`, and rejects `kit-exception:` reasons that say the kit lacks a component it now ships (FileInput/FileDropZone, Tile, Disclosure, ToggleChip, HoverCard, DateRangePicker, Progress tones, LegendMarker). Current sites are listed with a reason and may only shrink ([#19066](https://github.com/cheaper-inference/OmniRoute/issues/19066)) ([#19093](https://github.com/cheaper-inference/OmniRoute/pull/19093))
- **fix(dashboard):** With a stored palette or an explicit light/dark theme the page no longer ends up with 4 `theme-color` tags instead of 2: the pre-paint script now parks the server tags behind a copy instead of rewriting them, so React adopts them at hydration and the store removes the copies. The route progress bar glow uses the kit accent instead of the old indigo/coral, and the orphan `border-glow`, `.bg-vibrancy`, `.ant-table` and coral `::selection` fallback rules are gone from `globals.css` ([#19066](https://github.com/cheaper-inference/OmniRoute/issues/19066)) ([#19092](https://github.com/cheaper-inference/OmniRoute/pull/19092))
- **fix(ci):** main-green companion — 12 GB build heap + hosted-suite validator (#17350/#17851) and byte-pinned mirror tests (#17250) ([#18252](https://github.com/cheaper-inference/OmniRoute/pull/18252))
- **fix(api):** override stop_reason to tool_use when tool_calls arrive with finish_reason stop (#18270) ([#18280](https://github.com/cheaper-inference/OmniRoute/pull/18280)) — thanks @herjarsa
- **fix(responses):** include excluded cache writes in context usage ([#18361](https://github.com/cheaper-inference/OmniRoute/pull/18361)) — thanks @amirimatin
- **fix(dashboard):** display account/connection id in combo live studio cascade nodes ([#18374](https://github.com/cheaper-inference/OmniRoute/pull/18374)) — thanks @afonsoft
- **fix(api):** accept dashboard sessions on preset item routes ([#18386](https://github.com/cheaper-inference/OmniRoute/pull/18386)) — thanks @insoln
- **fix(sse):** prevent abandoned submissions from blocking the rate-limit queue ([#18526](https://github.com/cheaper-inference/OmniRoute/pull/18526)) — thanks @shevar0
- **fix(dashboard):** retain console history and pause updates for copying ([#18528](https://github.com/cheaper-inference/OmniRoute/pull/18528)) — thanks @shevar0
- **fix(translator):** hoist displaced tool results into their assistant turn ([#18709](https://github.com/cheaper-inference/OmniRoute/pull/18709)) — thanks @hongkongkiwi
- **fix(ci):** free hosted runner disk before the unit shards ([#18808](https://github.com/cheaper-inference/OmniRoute/pull/18808))
- **fix(dashboard):** keep the retry-delay select from squeezing its description ([#18830](https://github.com/cheaper-inference/OmniRoute/pull/18830)) — thanks @zhiru
- **fix(ci):** free hosted runner disk before the coverage merge ([#18845](https://github.com/cheaper-inference/OmniRoute/pull/18845))
- **fix(ci):** keep CodeRabbit path filters exclusion-only ([#18853](https://github.com/cheaper-inference/OmniRoute/pull/18853))
- **fix(i18n):** record the source hash when every mirror already carries it ([#18854](https://github.com/cheaper-inference/OmniRoute/pull/18854))
- **fix(ci):** check out docker-publish shallow and list tags with ls-remote ([#18884](https://github.com/cheaper-inference/OmniRoute/pull/18884))
- **fix(release):** clear the v3.8.53 tip reds (ui-kit recharts 3 type, Antigravity failover test) ([#18885](https://github.com/cheaper-inference/OmniRoute/pull/18885))
- **fix(ci):** publish docker manifests when runner-preflight is skipped ([#18887](https://github.com/cheaper-inference/OmniRoute/pull/18887))
- **fix(sse):** support foreign Undici Request brands in deadline wrapper ([#18890](https://github.com/cheaper-inference/OmniRoute/pull/18890)) — thanks @Bl0ck154
- **fix(test):** run the Antigravity silent-body failover watchdog on a virtual clock ([#18906](https://github.com/cheaper-inference/OmniRoute/pull/18906))
- **fix(i18n):** re-record hand-edited mirrors in the scheduled refresh ([#18951](https://github.com/cheaper-inference/OmniRoute/pull/18951))
- **fix(ci):** stop description edits on Mergify drafts from cancelling the queue run ([#18961](https://github.com/cheaper-inference/OmniRoute/pull/18961))
- **fix(routing):** consolidate portable multimodal transport and cooldown fixes ([#19032](https://github.com/cheaper-inference/OmniRoute/pull/19032)) — thanks @KeakReleaseBot
- **fix(combo):** drop the duplicate waitForCooldownAwareRetry import on the v3.8.53 tip ([#19062](https://github.com/cheaper-inference/OmniRoute/pull/19062))
- **fix(ci):** count only jobs a runner took in the Blacksmith budget ([#19155](https://github.com/cheaper-inference/OmniRoute/pull/19155))
- **fix(ci):** keep CodeRabbit off the vendored kit and train validation PRs ([#19159](https://github.com/cheaper-inference/OmniRoute/pull/19159))
### 📝 Maintenance
- **fix(release):** clear two release/v3.8.53 base-reds (#17219) — the open-sse typecheck now passes again (the Gemini prompt cache accounting from #12775 left a widened cache-mode literal in `chatCore.ts` and an `unknown` prompt count in the buffered Gemini parser; both are narrowed without changing behavior), and the strict counts gate is green: `PROVIDER_REFERENCE.md` is regenerated (377 providers), the README diagrams are regenerated from their generators, and the provider (376 → 377) and migration (211 → 213) counts are updated in the docs, skills, package description and the README database row of the 66 translations ([#18472](https://github.com/cheaper-inference/OmniRoute/pull/18472))
- **chore(quality):** the `check:build-scope` ceiling moves from 12000 to 13500 in-scope source files. The release branch reached 12267 through legitimate test growth (8296 test files), which turned the gate red on every PR; a leaked worktree or vendored copy (~12k files) is still caught (#17219) ([#18516](https://github.com/cheaper-inference/OmniRoute/pull/18516))
- **ci:** closing a PR now cancels its queued and running workflow runs, so they stop holding hosted-runner slots against the organization's concurrent-job limit. ([#18278](https://github.com/cheaper-inference/OmniRoute/pull/18278))
- **ci:** on pull requests the Coverage job no longer writes `lcov.info` or uploads to Codecov (informational, kept on push and the merge queue); the 60% coverage gate and the PR coverage summary are unchanged. ([#18336](https://github.com/cheaper-inference/OmniRoute/pull/18336))
- **ci:** the Quality Gates workflow no longer runs its own `Unit Tests fast-path` (4 shards) and `Vitest (fast-path)` jobs, which repeated the CI unit suite and vitest command-for-command on every PR since CI started running on release PRs. Each PR now uses about 62 fewer runner-minutes and 5 fewer hosted jobs. ([#18336](https://github.com/cheaper-inference/OmniRoute/pull/18336))
- **ci:** ordinary pull requests now run the fast CI lane (lint, build, unit, vitest, coverage, security, docs/i18n); E2E, Electron, package, integration, ecosystem, protocol-client and Bun lanes run in the Mergify merge queue, on push/dispatch, or on demand with `gh workflow run ci.yml --ref <branch>`. ([#18342](https://github.com/cheaper-inference/OmniRoute/pull/18342))
- **refactor(security):** combo step ids strip the edge separator with a quantifier-free regex, so CodeQL `js/polynomial-redos` (#1165) no longer flags `slugify()` in `src/lib/combos/steps.ts`; ids are unchanged and a new test pins them. ([#17906](https://github.com/cheaper-inference/OmniRoute/pull/17906))
- **ci:** add `.coderabbit.yaml` so CodeRabbit (free for public repositories) reviews non-draft PRs to release branches and main, skipping translated mirrors, lockfiles and generated files; pre-merge checks start as warnings during calibration. ([#18431](https://github.com/cheaper-inference/OmniRoute/pull/18431))
- **refactor(dashboard):** the pre-kit UI is gone — the legacy dashboard shell and its `UI_KIT_SHELL` cutover flag (the catalog is back to 111 flags), the Material Symbols font and npm package (an icon name missing from `ICON_MAP` now draws a neutral placeholder glyph and warns outside production), the shared `Footer` that nothing rendered, and the token aliases with no consumer (`--grad-brand`, `--color-accent-hover`, `--color-traffic-*`, `--table-row-*`, `--table-cell-border`) (KEA-7886).
- **deps:** align `eslint-config-next` (and its `@next/eslint-plugin-next`) with `next` at `16.4.2`; the security bump to `next` 16.4.2 left the lint config on `16.3.8`. Exact pin, lockfile-only change. ([#18143](https://github.com/cheaper-inference/OmniRoute/pull/18143))
- **deps(security):** raise the `qs` override to `^6.16.1` (resolves to 6.17.0), picking up the upstream fix for `qs.stringify` with `arrayFormat: "comma"` throwing an uncaught `TypeError`. Lockfile-only change. ([#18381](https://github.com/cheaper-inference/OmniRoute/pull/18381))
- **docs(i18n):** the 66 `docs/reference/ENVIRONMENT.md` mirrors carry the `OMNIROUTE_EGRESS_QUOTA_ROTATION_PROVIDERS` row #17074 added, so the source no longer shows as translation drift. ([#17919](https://github.com/cheaper-inference/OmniRoute/pull/17919))
- **ci:** the scheduled full Release-Green sweep runs unit, integration and vitest as hosted jobs with `ci.yml`'s own shards (8 unit, 2 integration, vitest MCP + UI) on one resolved commit, and the `omni-release` slot keeps the static gates and the package build, now with a 12 GB build heap. On the 8-core slot the suites took ~4h15 per sweep and failed timing tests on CPU starvation, and the webpack build died at the 8 GB default heap. ([#17350](https://github.com/cheaper-inference/OmniRoute/pull/17350))
- **chore(quality):** the Release-Green package lane gets a 40 min ceiling. `next build` was killed at 20 min on an idle runner on every scheduled run, failing `build:cli`, the package artifact and the tarball boot-smoke; three dashboard and lease guards broken by recent merges are green again (#17219) ([#18693](https://github.com/cheaper-inference/OmniRoute/pull/18693))
- Translate the 111 dashboard UI keys the UI-kit migration (#17951) left untranslated in 64 locales, re-sync the drifted FEATURE_FLAGS, ENVIRONMENT, REPOSITORY_MAP, DESIGN_SYSTEM, CODEBASE_DOCUMENTATION and ARCHITECTURE mirrors, and classify the VS Code tag connection read that #17360 moved into `src/lib/vscode/tagModels.ts` in the hard-lease inventory. ([#18168](https://github.com/cheaper-inference/OmniRoute/pull/18168))
- **chore(dashboard):** the vendored Keak CI UI Kit moves from 0.0.48 to 0.0.66 (`packages/ui-kit`, upstream `3b6ba89`). The dashboard now uses the kit's self-hosted Switzer for UI, display and code text, so the `@fontsource` Inter / Instrument Serif / IBM Plex Mono imports leave `src/app/globals.css` (the packages stay for the README diagrams). The snapshot also brings the kit's opt-in Editions appearance, required onboarding controls with private setup copying, named values in `Stat`, card tabs for onboarding setup, a sidebar shortcut guard and the integration / overview patterns. ([#18434](https://github.com/cheaper-inference/OmniRoute/pull/18434))
- **chore(dashboard): sync the vendored CI UI Kit to 0.0.80**: picks up the kit's `cloud-*` components, `DialogContent size="wide"`, `Field labelAside` and `Select` option icons. The dashboard doesn't use the new APIs yet, so nothing changes on screen. ([#18698](https://github.com/cheaper-inference/OmniRoute/pull/18698))
- **perf(build):** enable incremental TypeScript for the `open-sse` workspace typecheck, cutting a repeat `npm run check:open-sse-typecheck` run from ~25s to ~6s locally (the build-info file stays gitignored via the existing `.tsbuildinfo-*` rule). The gate still re-reports every diagnostic on a warm run — the compiler cache never suppresses errors. Completes the coverage gap left by the four root typecheck configs. ([#17147](https://github.com/diegosouzapw/OmniRoute/pull/17147)) — thanks @opensource-elearning
- **perf(logging):** the `HH:MM:SS` clock prefix in `streamHandler.ts` and `usageTracking.ts` is built with `padStart` instead of `toLocaleTimeString`, removing a locale-formatter call (~63 µs vs ~0.3 µs) from two hot-path log lines per request with identical output ([#17149](https://github.com/diegosouzapw/OmniRoute/pull/17149)) — thanks @opensource-elearning
- **perf(resilience):** `resolveResilienceSettings` is memoized by the settings object's identity (a `WeakMap` over the cached settings object), so the many per-request calls reuse one resolved result instead of re-normalizing every section (measured ~9 µs/call before vs ~0.4 µs after); a new settings object (after `updateSettings` or cache expiry) is resolved afresh ([#17153](https://github.com/diegosouzapw/OmniRoute/pull/17153)) — thanks @opensource-elearning
- **refactor(opencode):** the Opencode executor caches its `accounts` live view while the backing health Map is unchanged and refreshes it after public Map additions, removals, clearing or replacement, preserving retained health references and request writeback behavior; no speedup is claimed — the request path reads `listForRequest`, and a contract suite pins the cache semantics ([#17155](https://github.com/diegosouzapw/OmniRoute/pull/17155)) — thanks @opensource-elearning.
- **test(cache):** Wait for loaded provider data before asserting cache statistics, avoiding a race against the static tab label ([#17220](https://github.com/diegosouzapw/OmniRoute/pull/17220)) — thanks @pacocartones
- **ci(docker):** the multi-arch manifest job no longer dies when the best-effort `bun-web` image produces no digest — the stale-digest guard reports and skips a missing `/tmp/digests/<image>` directory instead of aborting under `pipefail`, so `omniroute:<version>` / `latest` manifests publish even when the optional Bun web image fails (v3.8.52 release run 39316002758) ([#17223](https://github.com/diegosouzapw/OmniRoute/pull/17223))
- **docs(mcp):** Correct the stale tool-cardinality activation note: `MCP_TOOL_ALLOW` and `MCP_TOOL_DENY` already filter the registered MCP tool manifest on an opt-in basis, with no runtime behavior change ([#17231](https://github.com/diegosouzapw/OmniRoute/pull/17231)) — thanks @fabioKoruja00 / @WendeelMarinho
- **chore(release):** the cycle bump now rewrites the README's current-version claims too — `scripts/release/bump-version-claims.mjs` moves the footer, the `` `vX+` `` compatibility floor, the `**vA → vX**` highlights range and llm.txt's "Current version" from the previous to the new version in the root files and their 66 `docs/i18n` mirrors, with the exact detector of the Docs sync strict gate and a `--check` pre-flight; `release/v3.8.53`'s README is fixed the same way (it was born red on "stale version") ([#17237](https://github.com/diegosouzapw/OmniRoute/pull/17237))
- **test(batch):** Exercise forbidden CSV mapping segments with actual mappings and generated-body assertions. ([#17248](https://github.com/diegosouzapw/OmniRoute/pull/17248)) — thanks @pacocartones
- **test(batch):** Cover JSONL tail validation, duplicates across sampling windows and overlapping head/tail samples. ([#17249](https://github.com/diegosouzapw/OmniRoute/pull/17249)) — thanks @pacocartones
- **ci(i18n):** translation drift the base branch already carried no longer fails a pull request — `check-translation-drift.mjs --base-report` partitions drift into inherited (reported) and introduced-by-this-PR (fails), `--root` checks another checkout, and `Docs Sync (Strict)` builds the base report from the PR's base commit; the push and nightly runs keep the full strict verdict. The two mirror unit tests now pin translated bytes instead of source freshness, `bump-version-claims.mjs --near/--old/--new` propagates a literal mechanical edit (a measured count next to its anchor) to the 66 mirrors with the state policy, and the executors count reads 156 in `ARCHITECTURE.md` / `CODEBASE_DOCUMENTATION.md` ([#17250](https://github.com/diegosouzapw/OmniRoute/pull/17250))
- **test(cursor):** Cover queued-successor lock preservation when keyed mutex cleanup precedes a third caller. ([#17251](https://github.com/diegosouzapw/OmniRoute/pull/17251)) — thanks @pacocartones
- **ci(docker):** an in-flight `release/v*` Docker publish is no longer cancelled by the next push — GitHub keeps one running and one pending run per branch, so the `next` / `next-web` channel publishes from a branch that merges faster than the ~2 h amd64 build (13 days without an image, #17230); tag, release and dispatch publishes keep cancelling as before ([#17254](https://github.com/diegosouzapw/OmniRoute/pull/17254))
- **ci(electron):** the shared Next standalone job reuses the `next-build` artifact the CI produced for the same commit (hosted build only as fallback), so the four desktop legs stop rebuilding Next themselves — the macOS arm64 leg alone took 4h41 on v3.8.52 ([#17275](https://github.com/diegosouzapw/OmniRoute/pull/17275))
- **chore(release):** `scripts/release/preflight-build.sh` runs, before the release freeze, the checks that only explode on the tag — README/llm.txt version claims, root and `electron/` lockfile sync (`npm ci --dry-run`), vitest, `build:cli` + `check:pack-boot`, and a 2-minute smoke of the Bun `runner-web` apt list on the Bun base image (`preflight-bun-apt-smoke.sh`) ([#17276](https://github.com/diegosouzapw/OmniRoute/pull/17276))
- **perf:** the hot-path settings and combo reads are served from short-TTL caches that settings/combo writes invalidate, the MITM settings, OpenAPI spec and storage-health routes use async `fs/promises` instead of blocking the event loop, and the preserved rate-limiter settings map is now a bounded 512-entry LRU ([#17304](https://github.com/diegosouzapw/OmniRoute/pull/17304)) — thanks @mayankjaiswal1707-spec
- **refactor(a2a):** `/.well-known/agent.json` and `/.well-known/agent-card.json` now share one `buildAgentCardMetadata()` (gateway name, description, version, capabilities and the six static skills) instead of two copy-pasted blocks; the `check:known-symbols` A2A-skills gate and the contributor docs now point at `src/lib/a2a/agentCardMetadata.ts` ([#17357](https://github.com/diegosouzapw/OmniRoute/pull/17357) - thanks @JxnLexn)
- **refactor(quota):** the SQLite and Redis quota stores now share one `buildPoolUsageSnapshot()` for the per-dimension/per-key pool usage arithmetic (equal-split fallback, percentage weights, burn rate) instead of two copy-pasted ~70-line bodies; behavior is unchanged ([#17362](https://github.com/diegosouzapw/OmniRoute/pull/17362) - thanks @JxnLexn)
- **refactor(sse):** the GitLab Duo and NLPCloud executors now share one `buildSynthesizedTextStream()` for their synthetic OpenAI text completion streams (role chunk, content chunk, stop chunk, `[DONE]`) instead of two identical copies; wire output is unchanged ([#17366](https://github.com/diegosouzapw/OmniRoute/pull/17366) - thanks @JxnLexn)
- **refactor(sse):** the Ollama NDJSON transformer assembles streamed tool-call fragments in a typed `appendToolCallDelta()` helper (index reuse, numeric-ID coercion, name/argument concatenation, completed-call ordering) instead of inside the line handler; behaviour is unchanged ([#17367](https://github.com/diegosouzapw/OmniRoute/pull/17367)) — thanks @JxnLexn
- **refactor(api):** the dashboard and management proxy-health `GET` routes share one `handleProxyHealthGet()` handler (auth-first order, window parsing, sweep/history enrichment, age clamp and sanitized errors unchanged), with an invalid-credential test for both endpoints ([#17368](https://github.com/diegosouzapw/OmniRoute/pull/17368)) — thanks @JxnLexn
- **refactor(api):** the Radar intel and offers sync routes share one `handleRadarFeedSync()` handler (feature-flag gate, auth, bounded body validation and sanitized 500 unchanged; each route keeps its own callback, failure message and CORS preflight) ([#17371](https://github.com/diegosouzapw/OmniRoute/pull/17371)) — thanks @JxnLexn
- **refactor(api):** the dashboard and management proxy bulk-assign routes share one `handleBulkProxyAssignment()` handler (auth still runs before JSON parsing; schema validation, key-to-account normalization, assignment and dispatcher-cache invalidation are unchanged) ([#17373](https://github.com/diegosouzapw/OmniRoute/pull/17373)) — thanks @JxnLexn
- **refactor(api):** the dashboard and management proxy-assignment `PUT` routes share one `handleProxyAssignmentPut()` handler (auth, schema validation, persistence and dispatcher-cache invalidation keep their order; each route keeps its own failure message); behaviour is unchanged ([#17374](https://github.com/diegosouzapw/OmniRoute/pull/17374)) — thanks @JxnLexn
- **refactor(radar):** the intel and offers feed syncs share one `readBoundedBytes()` leaf helper (10 MiB cap, Content-Length fast reject, stream cancel on overflow, buffered fallback and read-error propagation unchanged; signature verification untouched) ([#17377](https://github.com/diegosouzapw/OmniRoute/pull/17377)) — thanks @JxnLexn
- **refactor(sse):** the JSON container length calculation in `jsonSize` is split into array, object and fallback-serialization helpers, with identical exactness against `JSON.stringify(...).length` and lower cognitive complexity ([#17380](https://github.com/diegosouzapw/OmniRoute/pull/17380) - thanks @JxnLexn)
- **refactor(sse):** the completed Responses tool-item validation is separated from the stateful Codex tool-handoff watcher, with identical matching behaviour and lower cognitive complexity ([#17382](https://github.com/diegosouzapw/OmniRoute/pull/17382) - thanks @JxnLexn)
- **refactor(sse):** delta tool-call ID normalization is extracted from the final OpenAI stream chunk normalizer, with identical in-place conversion and finish-reason detection and lower cognitive complexity ([#17389](https://github.com/diegosouzapw/OmniRoute/pull/17389) - thanks @JxnLexn)
- **test(ci):** preserve reviewed catalog evidence in history fixtures, refresh authenticated model expectations and generated counts, and include missing mutation-test mappings ([#17550](https://github.com/cheaper-inference/OmniRoute/pull/17550)) — thanks @KiaroSama
- **test(oauth):** initialize the Codex transport before installing refresh-response test doubles, assert that controlled responses are consumed and close the fixture database after bounded cases ([#17550](https://github.com/cheaper-inference/OmniRoute/pull/17550)) — thanks @KiaroSama
- **test(ci):** coordinate keepalive rejection after response commitment and publish plugin fixture output atomically with an explicit CommonJS scope ([#17550](https://github.com/cheaper-inference/OmniRoute/pull/17550)) — thanks @KiaroSama
- **refactor(api):** move the per-request Responses keepalive frame into a helper so `/v1/responses` stays under the cognitive-complexity ratchet after #18197 (#17825) ([#18281](https://github.com/cheaper-inference/OmniRoute/pull/18281))
- **chore(build):** `OMNIROUTE_DIST_DIR` lets `scripts/build/prepublish.ts` assemble the whole CLI publish bundle (including the MCP, chatgpt-web, call-log, export and compression worker bundles) in a scratch directory instead of `dist/`; the step refuses a value pointing at `/` or the checkout ([#17828](https://github.com/cheaper-inference/OmniRoute/pull/17828) - thanks @megamen32)
- **refactor(routing):** Share decoded Combo attempt failures between retry and quota decisions while preserving retry hints, response identity, and public errors ([#17956](https://github.com/cheaper-inference/OmniRoute/pull/17956)) — thanks @MumuTW
- **refactor(auth):** Extract model authorization decisions and matching into the domain layer while preserving API-key, publication, group, and cache behavior ([#17959](https://github.com/cheaper-inference/OmniRoute/pull/17959)) — thanks @MumuTW
- **docs(auto-combo):** the `nadir` strategy section links to Nadir key creation and the setup guide ([#18028](https://github.com/diegosouzapw/OmniRoute/pull/18028)) — thanks @doramirdor
- **test(perplexity-web):** the stream error boundary test's delayed success hook case uses the mapped `pplx-sonar` model instead of the unmapped `pplx-auto-delayed-success-proof`, which #17822 now rejects with a local 404 before the stream starts ([#18232](https://github.com/diegosouzapw/OmniRoute/pull/18232)) — thanks @HouMinXi
- **test(compression):** cover unchanged live-zone histories for both Chat Completions `messages` and Responses `input`: frozen-prefix reuse, current request controls, no needless compressor call, and fail-open to full compression when `instructions` change. Regression coverage for existing behavior, with no production change ([#18304](https://github.com/cheaper-inference/OmniRoute/pull/18304)) — thanks @aldoeliacim
- **diagnostics(providers):** add opt-in `OMNIROUTE_MODEL_SYNC_TIMINGS=1` phase timings for model synchronization, separating discovery headers/body, managed import, stale combo checks and call-log completion with anonymous per-request correlation. Diagnostics omit catalog/account/error payloads and preserve normal sync behavior. This provides evidence for the latency investigation in #18335; it does not claim to resolve the reported delay. ([#18466](https://github.com/cheaper-inference/OmniRoute/pull/18466))
- **chore(pricing):** Remove six retired or unlisted NVIDIA model IDs from the shipped zero-price defaults instead of continuing to advertise those passthrough IDs as free. This does not change registered NVIDIA models or explicit operator pricing; current GPT-OSS alias handling remains in the separate NVIDIA follow-ups (#18500). ([#18502](https://github.com/cheaper-inference/OmniRoute/pull/18502)) — thanks @fabioKoruja00
- **fix(i18n):** the scheduled mirror refresh no longer throws away every translated locale when a few fail: `run-translation.mjs` exits 3 on a partial run, keeps each failed (source, locale) mirror and state record as it was and writes them to `--failures-json`, and `i18n-mirror-refresh.yml` opens the refresh PR with the translated mirrors and lists the failed units in its body. The Google engine now also retries a dropped connection (`fetch failed` with a socket cause) instead of failing the locale on the first try ([#18539](https://github.com/cheaper-inference/OmniRoute/pull/18539), refs [#18538](https://github.com/cheaper-inference/OmniRoute/issues/18538)) — thanks @HouMinXi
- **fix(release):** clear the `release/v3.8.53` base-reds left by the merge campaign (#17219): keep readable reasoning replay on OpenCode Responses targets, keep `acp_cancelled` on real Devin client cancels, bring `open-sse → src` edges back to 774, keep the provider-detail client bundle free of the DB, repair `build:cli` frontmatter, remove 66 duplicated i18n keys and re-sync provider and migration counts (#18625).
- **fix(release):** ten open-sse files import the numeric helpers from `open-sse/utils/numeric.ts` instead of the `src/shared/utils/numeric.ts` re-export, removing 10 open-sse → src edges; the seam test now fails if an engine file goes back to the re-export ([#18627](https://github.com/diegosouzapw/OmniRoute/pull/18627)) — thanks @AndresDiagoM
- **test(cli):** `cli-tray.test.ts` also points `APPDATA` at its temp dir, so on Windows it no longer sees the developer's real Startup folder ([#18645](https://github.com/diegosouzapw/OmniRoute/pull/18645)) — thanks @AndresDiagoM
- **test(cli):** `repro-9156.test.ts` resolves its own path with `fileURLToPath`, so it no longer builds a `D:\D:\...` path on Windows ([#18646](https://github.com/diegosouzapw/OmniRoute/pull/18646)) — thanks @AndresDiagoM
- **test(rate-limit):** `rate-limit-learned-cap-13594.test.ts` closes the SQLite handle before removing its temp dir, which Windows refused with `EPERM` ([#18647](https://github.com/diegosouzapw/OmniRoute/pull/18647)) — thanks @AndresDiagoM
- **fix(release):** clear the remaining `release/v3.8.53` base-reds: resync the provider (380) and migration (219) counts across `AGENTS.md`, `llm.txt`, the agent-skill catalog and the two generated skill files, make `scripts/cli/omniroute-sso.mjs` gate its entry point on `pathToFileURL` so it actually runs on Windows, and lift the transitive `node-forge` to 1.4.2 to bring the vulnerability ratchet from 30 back under its 27 baseline (#18658). — thanks @HouMinXi
- **ci(release):** Main-branch full package validation now shares a 40-minute budget, so a slow pack and boot run fails on one clear deadline ([#18750](https://github.com/cheaper-inference/OmniRoute/pull/18750)) — thanks @HouMinXi
- **ci(dashboard):** the weekly UI Kit upstream watch now warns and keeps one tracking issue open when it can't read upstream, and closes it once it can; the message says the token is missing only when it really is unset ([#18759](https://github.com/cheaper-inference/OmniRoute/pull/18759))
- **test(ui):** stop `api-key-redis-auth-cache` timing out on a loaded host by doing its imports and migrations while the file is collected, outside the hook and test deadlines ([#18762](https://github.com/cheaper-inference/OmniRoute/pull/18762))
- **refactor(dashboard):** permanent notices use the kit `Alert` `dismissible={false}` prop instead of a CSS hack that hid the button, `/login` and `/status` build their notices from the kit `Alert`, and the combo builder's 1/2/3 stage icons come from one icon family ([#18764](https://github.com/cheaper-inference/OmniRoute/pull/18764))
- **test(combo):** Port @skygunner's 28-case cooldown-expansion regression matrix from #17945 onto the #17880 landing, as promised in the #17945 review ([#18811](https://github.com/cheaper-inference/OmniRoute/pull/18811)) — thanks @skygunner
- **test(ui):** repair the `test:vitest:ui` base-red: align the API-key access editor "Save sends the full PATCH body" test with the `preferredConnections: []` payload added by #13102, and register a no-op `ResizeObserver` stub in the shared vitest UI setup so tests rendering the kit `Table` (KEA-7886) stop crashing under jsdom (base-red) ([#18306](https://github.com/cheaper-inference/OmniRoute/pull/18306))
- **style(dashboard):** paint the primary Button with the flat kit accent (grad-brand is no longer an image) (KEA-7886) (direct commit `10dc708d0d`)
- **style(dashboard):** bridge the OmniRoute tokens to the CI UI Kit, load Keak fonts, drop the grid wallpaper (KEA-7886) (direct commit `0afbbcb5c6`)
- **build(dashboard):** ui-kit follow-ups — brand export path, snapshot gate hardening, pre-commit hook (KEA-7886) (direct commit `7bcfe80d5c`)
- **build(dashboard):** vendor the CI UI Kit as packages/ui-kit with sync script and snapshot gate (KEA